关于javascript:vue源码02-computed-响应式-初始化访问更新过程

28次阅读

共计 16357 个字符,预计需要花费 41 分钟才能阅读完成。

导航

[[深刻 01] 执行上下文](https://juejin.im/post/684490…)
[[深刻 02] 原型链](https://juejin.im/post/684490…)
[[深刻 03] 继承](https://juejin.im/post/684490…)
[[深刻 04] 事件循环](https://juejin.im/post/684490…)
[[深刻 05] 柯里化 偏函数 函数记忆](https://juejin.im/post/684490…)
[[深刻 06] 隐式转换 和 运算符](https://juejin.im/post/684490…)
[[深刻 07] 浏览器缓存机制(http 缓存机制)](https://juejin.im/post/684490…)
[[深刻 08] 前端平安](https://juejin.im/post/684490…)
[[深刻 09] 深浅拷贝](https://juejin.im/post/684490…)
[[深刻 10] Debounce Throttle](https://juejin.im/post/684490…)
[[深刻 11] 前端路由](https://juejin.im/post/684490…)
[[深刻 12] 前端模块化](https://juejin.im/post/684490…)
[[深刻 13] 观察者模式 公布订阅模式 双向数据绑定](https://juejin.im/post/684490…)
[[深刻 14] canvas](https://juejin.im/post/684490…)
[[深刻 15] webSocket](https://juejin.im/post/684490…)
[[深刻 16] webpack](https://juejin.im/post/684490…)
[[深刻 17] http 和 https](https://juejin.im/post/684490…)
[[深刻 18] CSS-interview](https://juejin.im/post/684490…)
[[深刻 19] 手写 Promise](https://juejin.im/post/684490…)
[[深刻 20] 手写函数](https://juejin.im/post/684490…)

[[react] Hooks](https://juejin.im/post/684490…)

[[部署 01] Nginx](https://juejin.im/post/684490…)
[[部署 02] Docker 部署 vue 我的项目](https://juejin.im/post/684490…)
[[部署 03] gitlab-CI](https://juejin.im/post/684490…)

[[源码 -webpack01- 前置常识] AST 形象语法树](https://juejin.im/post/684490…)
[[源码 -webpack02- 前置常识] Tapable](https://juejin.im/post/684490…)
[[源码 -webpack03] 手写 webpack – compiler 简略编译流程](https://juejin.im/post/684490…)
[[源码] Redux React-Redux01](https://juejin.im/post/684490…)
[[源码] axios ](https://juejin.im/post/684490…)
[[源码] vuex ](https://juejin.im/post/684490…)
[[源码 -vue01] data 响应式 和 初始化渲染 ](https://juejin.im/post/684490…)
[[源码 -vue02] computed 响应式 – 初始化,拜访,更新过程 ](https://juejin.im/post/684490…)
[[源码 -vue03] watch 侦听属性 – 初始化和更新 ](https://juejin.im/post/684490…)
[[源码 -vue04] Vue.set 和 vm.$set ](https://juejin.im/post/684490…)
[[源码 -vue05] Vue.extend ](https://juejin.im/post/684490…)

[[源码 -vue06] Vue.nextTick 和 vm.$nextTick ](https://juejin.im/post/684790…)

前置常识

学习指标

  • computed 计算属性只有在 computed 被拜访时,才会去计算

    • 因为在 new Watcher 是 computed watcher 时,即 lazy=true 时,在构造函数中没有立刻执行 get()办法,而是在计算属性被拜访时触发 computed 的响应式 get 后,执行的 get 办法中回去调用 computed getter 函数
  • computed 计算属性具备缓存性能

    • 通过 dirty=true 时,才会去执行 watcher.evaluate()办法,才会执行 computed wawtcher 中的 get()即 computd 定义的函数,从新计算,计算完后,将 this.dirty=false
    • 下次再拜访时,会先判断 dirty, 是 false 就间接返回缓存的值
  • computed 的依赖必须是响应式数据,不然即便依赖变动不会触发 computed 从新计算
  • 即便 computed 的依赖变动了,然而 computed 计算的值没有变动的话,不会从新渲染
  • computed watcher 和 render watcher 和 dep 和 dep.subs 和 watcher.deps 之间简单的关系

    • computed 拜访过程

      • 拜访 computed,触发 computed 响应式的 get 函数,get 函数中判断如果 dirty=true,那么执行 computed watchet 的 evalute 办法,即 watcher 中的 get()办法,而后把 Dep.target = computed watcher, 执行 computed getter 函数就是用户指定的 computed 函数
      • 执行 computed getter 函数的时,因为有依赖的响应式的 data,所以又会触发 data 的 get 函数,执行 dep.depend(),就是把 computed watcher 中的 newDeps 中增加 computed 依赖项的 dep,同时向 computed 依赖项的 dep 的 subs 中增加 computed watcher,而后又把 Dep.target = targetStack 数组中的前一个 watcher,而后返回计算的后果,并且把 dirty=false
      • 而后判断 Dep.taret 存在,就执行 computed watcher 的 depend 办法,循环遍历 computed watcher 中的 deps,取出 dep,执行 dep.depend
      • 因为此时的 Dep.target = render watcher,所以 dep.depend 会向 render watcher 的 newDeps 中增加 data 的 dep,向 data 的 dep 中的 subs 中增加 render watcher,那么此时的 computed 计算属性的依赖项的 dep 中的 subs 就是 [computed watcher, render watcher] 这样就保障了渲染的时候,computed 先于 render 先执行,保障 computed 有值
    • comuted 更新过程

      • 下面的例子中,change 的过程,是扭转了 computed 的依赖,之后的一系列流程
      • 触发 computed 的响应式依赖的 dep.notify()办法,循环遍历该依赖的 dep.subs 数组中的每一个 watcher.update()办法,在下面的例子中 subs=[computed watcher, render watcher]所以先执行 computed watcher
      • compued watcher

        • 在 update 办法中,会判断 lazy=true?,因为 computed watcher 的 lazy=true,所以执行 dirty=true
      • render watcher

        • 在 update 办法中, lazy 不为 true, 会执行 queueWatcher(this), 就是调用渲染 watcher 从新求值和渲染

一些单词

internal:外部的

Scheduler:调度器
queue:队列
(flushSchedulerQueue : 刷新调度器队列)

computed 源码

(1) computed 的初始化

  • Vue.prototype._init => initState(vm) => initComputed(vm, opts.computed)

(1-1) initComputed(vm, opts.computed)

  • 次要做了以下事件

    • <font color=blue size=5>(1-1-1) new Watcher(vm, getter, noop, computedWatcherOptions) </font>

      • new 了一个 computed watcher
      • 参数

        • computedWatcherOptions

          • 如何晓得是一个 computed watcher => 次要通过第 4 个参数 computedWatcherOptions => {lazy: true},即 comoputed watcher 的 lazy 属性是 true
        • getter

          • 就是用户本人定义的 computed 对象中的函数
        • noop

          • 是一个空函数
      • 留神点:

        • 在 new Watcher 计算属性 watcher 的构造函数中

          • this.value=this.lazy ? undefined : this.get()
          • 因为计算属性 watcher 的 lazy=true,所以不会立刻去执行 get() 办法
          • <font color=blue> 那什么时候去执行呢 get()呢?</font>

            • <font color=blue> 执行机会就是在 template 中拜访了 computed</font>,因为 computed 又定义了响应式,拜访了 computed 属性就会执行 computed 的 get 办法,在 get 办法中会执行 watcher.evaluate() 办法,在外面就是去执行 get(),从而去计算 computed 的后果
    • <font color=blue size=5>(1-1-2) 把 computd 定义成响应式 </font>

      • defineComputed(vm, key, userDef) => Object.defineProperty(target, key, sharedPropertyDefinition) => sharedPropertyDefinition.get => createComputedGetter(key) => computedGetter
      • 也就是说拜访 computed 中的 this.xxxx 就会去执行 computedGetter 函数
    • <font color=blue size=5>(1-1-3) computedGetter – 这个办法很重要 !!!!!!!!!!!!!!!!!!!!!</font>

      • watcher.evaluate() 执行计算属性 watcher 中的 evaluate 办法

        • 当 dirty=true,并且 watcher 存在时,就会执行 computed watcher 的 evalute 办法
        • <font color=blue>evalute</font> 办法会执行 <font color=blue>get()</font> 办法,并将 <font color=blue>this.dirty</font> 改为 <font color=blue>false</font>

          • <font color=blue>get()</font>

            • <font color=blue>pushTarget(this)</font>

              • <font color=blue> 向 targetStack 数组中 push 一个 computed watcher</font>
              • <font color=blue> 将 Dep.target 指定为 computed watcher</font>
            • 执行用户在 computed 对象中定义的办法,即 getter 办法 newName

              • 比方 computed: {newName() {return this.name + 'new'}}
              • 留神:

                • 在这过程中又会触发 data 对象的影响式,即 this.name 触发响应式 data 中的 get 函数,因为拜访了 data 的 name 属性
                • data,computed 都有本人的响应式
                • 这里 data 的响应式又会收集计算属性的 watcher,这个放在前面的计算属性的拜访流程中去梳理

                  • 次要就是

                    • 向 computed watcher 的 newDeps 中增加 render watcher 的 dep
                    • 向 render watcher 的依赖的属性的 dep 的 subs 中增加 computed watcher
                    • 详见下文
      • watcher.depend() 执行计算属性 watcher 的 depend 办法

        • 放在上面拜访流程一起剖析
  • 源码
  • Vue.prototype._init => initState(vm) => initComputed(vm, opts.computed)
  • initComputed – scr/core/instance/state.js

    initComputed - scr/core/instance/state.js
    ---
    
    function initComputed (vm: Component, computed: Object) {const watchers = vm._computedWatchers = Object.create(null)
    // 申明 watchers 和 _computedWatchers 为一个空对象
    
    const isSSR = isServerRendering()
    // 是否是 ssr 环境
    
    for (const key in computed) {const userDef = computed[key]
      // userDef 是 computed 的 getter 函数
    
      const getter = typeof userDef === 'function' ? userDef : userDef.get
      // getter
        // computed getter 能够是函数 或者 具备 get 办法的对象
        // 这里个别都是函数,并且该函数须要 return 一个值
    
      if (process.env.NODE_ENV !== 'production' && getter == null) {
        warn(`Getter is missing for computed property "${key}".`,
          vm
        )
      }
      // 如果不是函数或者对象就报正告
    
    
      if (!isSSR) {
        // create internal watcher for the computed property.
        // 非 ssr 环境,即浏览器环境,就新建 computed watcher
        // computed watcher
          // computedWatcherOptions = {lazy: true}
          // getter = 用户自定义的 computed 对象中的函数
    
        watchers[key] = new Watcher(
          vm,
          getter || noop, // 用户自定义的 computed 对象中的函数,即 computed getter
          noop,
          computedWatcherOptions, //  {lazy: true}
        )
      }
    
      // component-defined computed properties are already defined on the
      // component prototype. We only need to define computed properties defined
      // at instantiation here.
      // 在 vue.extends 和 new Vue() 过程中都定义了响应式的 computed
    
      if (!(key in vm)) {defineComputed(vm, key, userDef)
        // defineComputed 将 computed 变成响应式
    
      } else if (process.env.NODE_ENV !== 'production') {
        // 解决重名的状况,在 props,data,computed 不能用重名的 key
        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)
        }
      }
    }
    }
  • defineComputed – scr/core/instance/state.js

    defineComputed - scr/core/instance/state.js
    ---
    
    export function defineComputed (
    target: any,
    key: string,
    userDef: Object | Function
    ) {const shouldCache = !isServerRendering()
    // shouldCache 如果在浏览器环境就是 true
    
    if (typeof userDef === 'function') {
      sharedPropertyDefinition.get = shouldCache
        ? createComputedGetter(key) // 定义 computed 被拜访时,触发的 get
        : createGetterInvoker(userDef)
      sharedPropertyDefinition.set = noop
    } else {
      // userDef 不是 function,咱们间接疏忽
      sharedPropertyDefinition.get = userDef.get
        ? shouldCache && userDef.cache !== false
          ? createComputedGetter(key)
          : createGetterInvoker(userDef.get)
        : noop
      sharedPropertyDefinition.set = userDef.set || noop
    }
    
    if (process.env.NODE_ENV !== 'production' &&
        sharedPropertyDefinition.set === noop) {sharedPropertyDefinition.set = function () {
        warn(`Computed property "${key}" was assigned to but it has no setter.`,
          this
        )
      }
    }
    
    Object.defineProperty(target, key, sharedPropertyDefinition)
    // 定义响应式 computed
      // 1. 当通过 this.xxxx 拜访 computed,就会触发 sharedPropertyDefinition 对象中的 get
      // 2. get 其实就是上面 createComputedGetter 返回的 computedGetter 函数
    }
  • createComputedGetter – scr/core/instance/state.js

    createComputedGetter - scr/core/instance/state.js
    ---
    
    function createComputedGetter (key) {return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]
      // 取出每一个 computed watcher
    
      if (watcher) {if (watcher.dirty) {
          // watcher.dirty
            // 1. 默认初始化时,comoputed watcher 的 dirty=true
            // 2. 当 dirty=true 就会执行 watcher.evaluate()
            // 3. watcher.evaluate() 执行完后,dirty=false
            // 总结:dirty=true => watcher.evaluate() => dirty=false
    
          watcher.evaluate()
          // watcher.evaluate()
            // 1. 会去执行 computed watcher 中的 get()
              // pushTarget(this)
                // 1. 将 computed watcher 增加到  targetStack 数组中
                // 2. 将 Dep.target = computed watcher
              // 执行 this.getter.call(vm, vm) 即用户自定义的 computed 对象中的办法
                // 1. 列如:computed: {newName() {return this.name + 'new'}}
                // 2. 因为:computed 的 newName 办法中,依赖了 data 中的 this.name,即拜访到了 this.name 就会触发 data 响应式的 get 办法
                // 3. 所以:ata 响应式的 get 办法执行过程如下
                  // 获取到了 this.name 的值
                  // 此时,Dep.target 是 computed watcher
                  // 而后执行 this.name 对象的 dep 类的 depend 办法进行依赖收集
                    // 向 computed watcher 的 newDeps 中增加 render watcher 的 dep
                    // 向 render watcher 的 subs 中增加 computed watcher
              //  popTarget()
                // 1. targetStack.pop()将 computed watcher 从 targetStack 数组中删除
                // 2. 并且将 Dep.target 指定为数组中的前一个 watcher,没有了就是 undefined
            // 2. 将 dirty=false
    
          // evaluate () {//   this.value = this.get()
          //   this.dirty = false
          // }
    
          // get () {//   pushTarget(this)
          //   let value
          //   const vm = this.vm
          //   try {//     value = this.getter.call(vm, vm)
          //   } catch (e) {//     if (this.user) {//       handleError(e, vm, `getter for watcher "${this.expression}"`)
          //     } else {
          //       throw e
          //     }
          //   } finally {
          //     // "touch" every property so they are all tracked as
          //     // dependencies for deep watching
          //     if (this.deep) {//       traverse(value)
          //     }
          //     popTarget()
          //     this.cleanupDeps()
          //   }
          //   return value
          // }
    
        }
    
        if (Dep.target) {watcher.depend()
    
          // depend () {
          //   let i = this.deps.length
          //   while (i--) {//     this.deps[i].depend()
          //   }
          // }
    
          
        }
        return watcher.value
      }
    }
    }
  • Watcher – scr/core/observer/watcher.js

    Watcher - scr/core/observer/watcher.js
    ---
    
    export default class Watcher {
    vm: Component;
    expression: string;
    cb: Function;
    id: number;
    deep: boolean;
    user: boolean;
    lazy: boolean;
    sync: boolean;
    dirty: boolean;
    active: boolean;
    deps: Array<Dep>;
    newDeps: Array<Dep>;
    depIds: SimpleSet;
    newDepIds: SimpleSet;
    before: ?Function;
    getter: Function;
    value: any;
    
    constructor (
      vm: Component,
      expOrFn: string | Function,
      cb: Function,
      options?: ?Object,
      isRenderWatcher?: boolean
    ) {
      this.vm = vm
      if (isRenderWatcher) {vm._watcher = this}
      vm._watchers.push(this)
      // options
      if (options) {
        this.deep = !!options.deep
        this.user = !!options.user
        this.lazy = !!options.lazy
        this.sync = !!options.sync
        this.before = options.before
      } else {this.deep = this.user = this.lazy = this.sync = false}
      this.cb = cb
      this.id = ++uid // uid for batching
      this.active = true
      this.dirty = this.lazy // for lazy watchers
      this.deps = []
      this.newDeps = []
      this.depIds = new Set()
      this.newDepIds = new Set()
      this.expression = process.env.NODE_ENV !== 'production'
        ? expOrFn.toString()
        : ''
      // parse expression for getter
      if (typeof expOrFn === 'function') {this.getter = expOrFn} else {this.getter = parsePath(expOrFn)
        if (!this.getter) {
          this.getter = noop
          process.env.NODE_ENV !== 'production' && warn(`Failed watching path: "${expOrFn}" ` +
            'Watcher only accepts simple dot-delimited paths.' +
            'For full control, use a function instead.',
            vm
          )
        }
      }
      this.value = this.lazy
        ? undefined
        : this.get()}
    
    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    get () {pushTarget(this)
      let value
      const vm = this.vm
      try {value = this.getter.call(vm, vm)
      } catch (e) {if (this.user) {handleError(e, vm, `getter for watcher "${this.expression}"`)
        } else {throw e}
      } finally {
        // "touch" every property so they are all tracked as
        // dependencies for deep watching
        if (this.deep) {traverse(value)
        }
        popTarget()
        this.cleanupDeps()}
      return value
    }
    
    /**
     * Add a dependency to this directive.
     */
    addDep (dep: Dep) {
      const id = dep.id
      if (!this.newDepIds.has(id)) {this.newDepIds.add(id)
        this.newDeps.push(dep)
        if (!this.depIds.has(id)) {dep.addSub(this)
        }
      }
    }
    
    /**
     * Clean up for dependency collection.
     */
    cleanupDeps () {
      let i = this.deps.length
      while (i--) {const dep = this.deps[i]
        if (!this.newDepIds.has(dep.id)) {dep.removeSub(this)
        }
      }
      let tmp = this.depIds
      this.depIds = this.newDepIds
      this.newDepIds = tmp
      this.newDepIds.clear()
      tmp = this.deps
      this.deps = this.newDeps
      this.newDeps = tmp
      this.newDeps.length = 0
    }
    
    /**
     * Subscriber interface.
     * Will be called when a dependency changes.
     */
    update () {
      /* istanbul ignore else */
      if (this.lazy) {this.dirty = true} else if (this.sync) {this.run()
      } else {queueWatcher(this)
      }
    }
    
    /**
     * Scheduler job interface.
     * Will be called by the scheduler.
     */
    run () {if (this.active) {const value = this.get()
        if (
          value !== this.value ||
          // Deep watchers and watchers on Object/Arrays should fire even
          // when the value is the same, because the value may
          // have mutated.
          isObject(value) ||
          this.deep
        ) {
          // set new value
          const oldValue = this.value
          this.value = value
          if (this.user) {
            try {this.cb.call(this.vm, value, oldValue)
            } catch (e) {handleError(e, this.vm, `callback for watcher "${this.expression}"`)
            }
          } else {this.cb.call(this.vm, value, oldValue)
          }
        }
      }
    }
    
    /**
     * Evaluate the value of the watcher.
     * This only gets called for lazy watchers.
     */
    evaluate () {this.value = this.get()
      this.dirty = false
    }
    
    /**
     * Depend on all deps collected by this watcher.
     */
    depend () {
      let i = this.deps.length
      while (i--) {this.deps[i].depend()}
    }
    
    /**
     * Remove self from all dependencies' subscriber list.
     */
    teardown () {if (this.active) {
        // remove self from vm's watcher list
        // this is a somewhat expensive operation so we skip it
        // if the vm is being destroyed.
        if (!this.vm._isBeingDestroyed) {remove(this.vm._watchers, this)
        }
        let i = this.deps.length
        while (i--) {this.deps[i].removeSub(this)
        }
        this.active = false
      }
    }
    }

(2) computed 的拜访过程

  • 案例

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./vue.js"></script>
    </head>
    <body>
    <div id="root">
      <div>{{newName}}</div>
      <button @click="change">change</button>
    </div>
    <script>
      new Vue({
        el: '#root',
        data: {name: 'ssssssssssssss'},
        computed: {newName() {return this.name + 'new'}
        },
        methods: {change() {this.name = '222222222222222'}
        }
      })
    </script>
    </body>
    </html>
  • vm._update(vm._render(), hydrating) 过程中,当在 template 模板中应用了 computed 对象中的 key 的时候,就会触发 computed 响应式对象的 get 办法
  • computed 的响应式 get 办法就是 computedGetter 办法,在该办法中,会判断 wathcer 和 watcher.dirty 是否存在,存在证实就是 computed watcher,就会执行 computed wathcer 的 watcher.evaluate() 办法
  • watcher.evaluate()办法就会执行 computed watcher 中的 get()办法,并将 this.dirty 改为 false
  • watcher.evaluate()

    • get()办法

      • 调用 <font color=red>pushTarget(this)</font>

        • <font color=red> 向 targetStack 数组中 push 一个 computed watcher</font>
        • <font color=red> 将 Dep.target 指定为 computed watcher</font>
      • 而后调用 computed getter 办法,即用户本人定义的 computed 对象中的办法

        • 比方:computed: {newName() {return this.name + 'new'}}
        • 因为:computed 的 newName 办法中,依赖了 data 中的 this.name,即拜访到了 this.name 就会触发 data 响应式的 get 办法
        • 所以:<font color=DarkOrChid>data 响应式的 get 办法执行过程如下:</font>

          • 获取到了 this.name 的值
          • 此时,Dep.target 是 computed watcher

            • 而后执行 this.name 对应的 dep 类的 depend 办法进行依赖收集

              • <font color=DarkOrChid> 向 computed watcher 的 newDeps 中增加 render watcher 的对应的 data 属性的 dep</font>
              • <font color=DarkOrChid> 向 render watcher 对应的 data 属性的 dep 实例的 subs 中增加 computed watcher</font>
              • <font color=DarkOrChid> 等于说 data 的 this.name 和 computed Watcher 具备同一个 dep 实例 </font>
            • 执行完下面的步骤后 dep.subs 和 computed watcher.newDeps 的状态是

              • this.name 对应的 dep 实例中的 subs = [computedWatcher]
              • computed watcher 中的 newDeps = [下面的 this.name 对应的 dep]
          • 返回 name 的值
      • 调用 <font color=red>popTarget()</font>

        • <font color=red>targetStack.pop()将 computed watcher 从 targetStack 数组中删除 </font>
        • <font color=red> 并且将 Dep.target 指定为数组中的前一个 watcher</font>

          • <font color=red>Dep.target = render watcher</font>
          • 在下面的例子中 targetStack 数组中在执行 computed 的 getter 办法时一共有两个成员
          • 第一个:render watcher
          • 第二个:computed watcher
          • pop 后还剩一个 render watcher
      • 最初返回 computed 计算失去的后果值
  • watcher.depend()

    • 当 Dep.target 存在时,才会执行 watcher.depend()
    • 下面执行完,<font color=DarkOrChid>Dep.target = render watcher</font>
    • watcher.depend()

      • 而后执行 <font color=DarkOrChid>compute watcher 中的 watcher.depend() 办法 </font>
      • 而后,<font color=DarkOrChid> 从后往前,顺次取出 computed watcher 中 deps 数组中的 dep,执行 dep.depend()</font>

        • 留神:下面 computed watcher 中的 deps 中的 dep,就是 this.name 对象的 dep,外面的 subs 数组中只有一个 computedWatcher
        • 比照:在 data 对象的属性被拜访的时候,也会执行 data 对应的属性的 dep.depend()
      • <font color=DarkOrChid>Dep.target.addDep(this)</font>

        • <font color=DarkOrChid> 此时的 Dep.targt 是 render watcher</font>,因为 popTarget() 操作 pop 出 computed watcher 后就只剩 render watcher 了
        • addDep 前

          • render watcher 中的 deps 是空数组
          • render watcher 中的 newDeps 是空数组
        • addDep 次要做两件事件

          • <font color=DarkOrChid> 向 render watcher 的 newDeps 中增加 该 render watcher 对应的 datad 属性的 dep</font>
          • <font color=DarkOrChid> 向 render watcher 对应的 data 属性对应的 dep 类的 subs 中增加 render watcher</font>
          • 增加后,data 的 dep.subs = [computed watcher, render watcher]

            • <font color=red> 这样当 this.name 属性批改后触发对应的 set 函数,就会触发 dep.notify,而后循环 sub 中的 watcher,执行 watcher.update()办法 </font>
            • <font color=red>[computed watcher, render watcher]这样的程序保障了在 render 的时候,computed 肯定有值 </font>

(3) computed 的更新过程

  • 下面的例子中,change 的过程,是扭转了 computed 的依赖,之后的一系列流程
  • 触发 computed 的响应式依赖的 dep.notify()办法,循环遍历该依赖的 dep.subs 数组中的每一个 watcher.update()办法,在下面的例子中 subs=[computed watcher, render watcher]所以先执行 computed watcher
  • compued watcher

    • 在 update 办法中,会判断 lazy=true?,因为 computed watcher 的 lazy=true,所以执行 dirty=true
  • render watcher

    • 在 update 办法中, lazy 不为 true, 会执行 queueWatcher(this), 就是调用渲染 watcher 从新求值和渲染

材料

理论案例 - 避坑 https://juejin.im/post/684490…
具体 https://juejin.im/post/684490…
源码版 https://juejin.im/post/684490…
computed watcher https://juejin.im/post/684490…

正文完
 0