Vue实例创建之computed

前言

上篇文章就new Vue()过程中的基本处理流程有了了解,并对data和挂载部分进行了分析,本次就针对于computed进行具体的分析。
计算属性computed的提出三个问题点:

  • Vue对于computed的具体处理
  • computed与data有何区别
  • computed是如何收集依赖的

computed

computed的处理逻辑都是由initComputed而起,具体的处理逻辑如下:
这里写图片描述

从上面可知,如同每个data会有对应的Watcher实例用于实现data响应式:

每个computed也会有一个Watcher对象实例来实现computed的响应式

当computed中定义的属性不与data和props中同名,则会调用defineComputed来定义computed,实际上该函数实现类似defineReactive功能:

defineReactive是实现data响应式的核心,即Object.defineProperty拦截data中数据
defineComputed亦是如此,是实现computed响应式的核心,即Object.defineProperty拦截computed中属性

具体看看defineComputed的处理逻辑:
这里写图片描述

从上面可知,defineComputed就是使用Object.defineProperty来实现响应式的,针对浏览器环境需要构建特殊处理getter部分,即使用createComputedGetter来构建getter。

实际上createComputedGetter内部构建的getter实际会调用Watcher对象的depend和exaluate方法,具体处理如下:

function createComputedGetter (key) {
  return function computedGetter () {
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      watcher.depend();
      return watcher.evaluate()
    }
  }
}

这就是new Vue过程中关于computed整个处理过程,可能要问:

  • 没有关于computed与data的区别啊?
  • 没有computed如何收集依赖的啊?

实际上这两个问题都是在Watcher相关的处理:new Watcher、watcher、depend、evaluate这些过程,接下来就computed相关来看上面三个过程的具体处理。

Watcher处理computed相关

首先是通过Watcher创建监听对象,涉及到computed如下:

// 标记是否是计算属性
this.computed = !!options.computed;
this.dirty = this.computed;
if (this.computed) {
    this.value = undefined;
    // 建立依赖对象
    this.dep = new Dep();
}

实际上关于computed,new Watcher时候主要就是上面的处理,那是如何收集依赖的呢?

实际上这部分就是computed的getter部分来实现的,从之前分析中可以得到:getter中实际上会调用Watcher实例对象的depend和evaluate。

Watcher.prototype.depend
该实例方法内部的处理如下:

 if (this.dep && Dep.target) {
    // 实际上调用Dep对象的depend实例方法
    this.dep.depend();
  }

其实在上篇文章中就分析Dep对象的depend方法的具体处理,如下:

Dep.target.addDep(this)调用Watcher对象实例方法addDep

Watcher的addDep实例方法的作用是:

建立Dep与Watcher的联系,即Dep中subs属性保存Watcher对象

Watcher.prototype.evaluate
该实例方法的具体处理是执行watcher实例的get方法:

  if (this.dirty) {
    this.value = this.get();
    this.dirty = false;
  }
  return this.value

实际上该实例方法是computed收集依赖和缓存计算属性的触发点

this.get()实际是getter函数(就是开发者定义的计算属性的函数或get函数)的执行,此时获取data的变量会触发data的getters方法,即watcher收集data依赖的过程,在上篇文章中就分析了这个过程中会调用dep.depend,将watcher与dep建立联系

上面就是computed如何收集依赖,最后的收集与watcher收集data依赖是相同的处理,通过上面的步骤与computed Watcher建立联系。

再来说说computed的缓存效果,实现缓存效果实际上就是this.dirty控制的:

this.dirty的作用,当this.dirty为true或调用getter方法,触发data的getter获取当前值计算,this.dirty为false就会直接返回当前Watcher实例中value值

当computed依赖发生变化,就会将触发setter方法,这边的处理在上文中有具体分析,实际上是会触发:

setter -> dep.notify() -> Watcher实例的update()

而当依赖发生变化,实际上会触发所有包含该Dep对象的Watcher实例对象的update()实例方法。

update这边所涉及的处理比较复杂,现在只要记住这边会重新计算computed值以及重新渲染视图即可,之后会专门介绍这块的处理。

总结

通过上面的分析,可知下面几点:

  • 每个computed相对应对应一个Watcher实例对象
  • computed收集依赖实际上是在其getter中触发的

    computed getter -> 依赖的getter -> dep.depend建立了当前dep与Watcher的联系

  • computed与data的区别是存在缓存,缓存的控制实际上是Watcher中dirty属性来控制的,false时就取缓存中的值

猜你喜欢

转载自blog.csdn.net/s1879046/article/details/82381468
今日推荐