乐趣区

关于vue.js:Vue-computed源码解析

1. 每个 computed 属性都会生成对应的观察者(Watcher 实例),观察者存在 values 属性和 get 办法。computed 属性的 getter 函数会在 get 办法中调用,并将返回值赋值给 value。初始设置 dirty 和 lazy 的值为 true,lazy 为 true 不会立刻 get 办法(懒执行),而是会在读取 computed 值时执行。

function initComputed (vm, computed) {var watchers = vm._computedWatchers = Object.create(null);// 寄存 computed 的观察者
  var isSSR = isServerRendering();
  for (var key in computed) {var userDef = computed[key];
    var getter = typeof userDef === 'function' ? userDef : userDef.get;
    ...
    watchers[key] = new Watcher(// 生成观察者(Watcher 实例)vm,
      getter || noop,// getter 将在观察者 get 办法中执行
      noop,
      computedWatcherOptions // {lazy: true} 懒执行,暂不执行 get 办法,当读取 computed 属性值执行
    );
    ...
    defineComputed(vm, key, userDef);
    ...
  }
}

2. 将 computed 属性增加到组件实例上,并通过 get、set 获取或者设置属性值,并且重定义 getter 函数,

function defineComputed(target, key, userDef) {var shouldCache = !isServerRendering();
    ...
    sharedPropertyDefinition.get = shouldCache // 重定义 getter 函数
        ? createComputedGetter(key)
        : createGetterInvoker(userDef);
   ...
    Object.defineProperty(target, key, sharedPropertyDefinition); // 将 computed 属性增加到组件实例上
}
// 重定义的 getter 函数
function createComputedGetter(key) {return function computedGetter() {var watcher = this._computedWatchers && this._computedWatchers[key];
        if (watcher) {if (watcher.dirty) {
                // true,懒执行
                watcher.evaluate(); // 执行 watcher 办法后设置 dirty 为 false}
            if (Dep.target) {watcher.depend();
            }
            return watcher.value; // 返回观察者的 value 值
        }
    };
}

3. 页面初始渲染时,读取 computed 属性值,触发重定义后的 getter 函数。因为观察者的 dirty 值为 true,将会调用 get 办法,执行原始 getter 函数。getter 函数中会读取 data(响应式)数据,读取数据时会触发 data 的 getter 办法,会将 computed 属性对应的观察者增加到 data 的依赖收集器中(用于 data 变更时告诉更新)。观察者的 get 办法执行实现后,更新观察者的 value 值,并将 dirty 设置为 false,示意 value 值已更新,之后在执行观察者的 depend 办法,将下层观察者(该观察者蕴含页面更新的办法,办法中读取了 computed 属性值)也增加到 getter 函数中 data 的依赖收集器中(getter 中的 data 的依赖器收集器蕴含 computed 对应的观察者,以及蕴含页面更新办法(调用了 computed 属性)的观察者),最初返回 computed 观察者的 value 值。

4. 当更改了 computed 属性 getter 函数依赖的 data 值时,将会依据之前依赖收集的观察者,顺次调用观察者的 update 办法,先调用 computed 观察者的 update 办法,因为 lazy 为 true,将会设置观察者的 dirty 为 true,示意 computed 属性 getter 函数依赖的 data 值发生变化,但不调用观察者的 get 办法更新 value 值。再调用蕴含页面更新办法的观察者的 update 办法,在更新页面时会读取 computed 属性值,触发重定义的 getter 函数,此时因为 computed 属性的观察者 dirty 为 true,调用该观察者的 get 办法,更新 value 值,并返回,实现页面的渲染。

5.dirty 值初始为 true,即首次读取 computed 属性值时,依据 setter 计算属性值,并保留在观察者 value 上,而后设置 dirty 值为 false。之后读取 computed 属性值时,dirty 值为 false,不调用 setter 从新计算值,而是间接返回观察者的 value,也就是上一次计算值。只有当 computed 属性 setter 函数依赖的 data 发生变化时,才设置 dirty 为 true,即下一次读取 computed 属性值时调用 setter 从新计算。也就是说,computed 属性依赖的 data 不发生变化时,不会调用 setter 函数从新计算值,而是读取上一次计算值。

退出移动版