我们来看看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的函数。