我們來看看computed的實現。最簡單的一個demo如下:
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div id="app"><div name="test">{{computeA}}</div></div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">new Vue({el: '#app',data: function () {return {firstName: 111,lastName: 222}},computed: {computeA: function () {return this.firstName + ' ' + this.lastName}},created(){setTimeout(() => {this.firstName = 333;},1000)}
})
</script>
</html>
1在初始化實例創建響應式的時候。對options中的computed做了特殊處理:
function initComputed (vm, computed) {var watchers = vm._computedWatchers = Object.create(null);for (var key in computed) {var userDef = computed[key];var getter = typeof userDef === 'function' ? userDef : userDef.get;{if (getter === undefined) {warn(("No getter function has been defined for computed property \"" + key + "\"."),vm);getter = noop;}}// create internal watcher for the computed property.watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);//為每一個computed項目訂制一個watcher// component-defined computed properties are already defined on the// component prototype. We only need to define computed properties defined// at instantiation here.if (!(key in vm)) {defineComputed(vm, key, userDef);} else {if (key in vm.$data) {warn(("The computed property \"" + key + "\" is already defined in data."), vm);} else if (vm.$options.props && key in vm.$options.props) {warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);}}function defineComputed (target, key, userDef) {if (typeof userDef === 'function') {sharedPropertyDefinition.get = createComputedGetter(key);sharedPropertyDefinition.set = noop;} else {sharedPropertyDefinition.get = userDef.get? userDef.cache !== false? createComputedGetter(key): userDef.get: noop;sharedPropertyDefinition.set = userDef.set? userDef.set: noop;}Object.defineProperty(target, key, sharedPropertyDefinition);
}function createComputedGetter (key) {//構造該computed的get函數return function computedGetter () {var watcher = this._computedWatchers && this._computedWatchers[key];if (watcher) {if (watcher.dirty) {watcher.evaluate();//收集該watcher的訂閱}if (Dep.target) {watcher.depend();//同一為這一組訂閱再加上組件re-render的訂閱(該訂閱負責更新組件)}return watcher.value}}
}
組件初始化的時候。computed項和data中的分別建立響應式。data中的數據直接對屬性的get,set做數據攔截。而computed則建立一個新的watcher,在組件渲染的時候。先touch一下這個computed的getter函數。將這個watcher訂閱起來。這里相當于這個computed的watcher訂閱了firstname和lastname。touch完后。Dep.target此時又變為之前那個用于更新組件的。再通過watcher.depend()將這個組統一加上這個訂閱。這樣一旦firstname和lastname變了。同時會觸發兩個訂閱更新。其中一個便是更新組件。重新re-render的函數。