[vue源碼02] computed 響應(yīng)式 - 初始化,訪問(wèn),更新過(guò)程

image

導(dǎo)航

[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)

[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI

[源碼-webpack01-前置知識(shí)] AST抽象語(yǔ)法樹
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫webpack - compiler簡(jiǎn)單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染
[源碼-vue02] computed 響應(yīng)式 - 初始化,訪問(wèn),更新過(guò)程
[源碼-vue03] watch 偵聽屬性 - 初始化和更新
[源碼-vue04] Vue.set 和 vm.$set
[源碼-vue05] Vue.extend

[源碼-vue06] Vue.nextTick 和 vm.$nextTick

前置知識(shí)

學(xué)習(xí)目標(biāo)

  • computed計(jì)算屬性只有在computed被訪問(wèn)時(shí),才會(huì)去計(jì)算
    • 因?yàn)樵趎ew Watcher是computed watcher時(shí),即lazy=true時(shí),在構(gòu)造函數(shù)中沒有立即執(zhí)行g(shù)et()方法,而是在計(jì)算屬性被訪問(wèn)時(shí)觸發(fā)computed的響應(yīng)式get后,執(zhí)行的get方法中回去調(diào)用computed getter函數(shù)
  • computed計(jì)算屬性具有緩存功能
    • 通過(guò)dirty=true時(shí),才會(huì)去執(zhí)行watcher.evaluate()方法,才會(huì)執(zhí)行computed wawtcher中的get()即computd 定義的函數(shù),從新計(jì)算,計(jì)算完后,將this.dirty=false
    • 下次再訪問(wèn)時(shí),會(huì)先判斷dirty,是false就直接返回緩存的值
  • computed的依賴必須是響應(yīng)式數(shù)據(jù),不然即使依賴變化不會(huì)觸發(fā)computed重新計(jì)算
  • 即使computed的依賴變化了,但是computed計(jì)算的值沒有變化的話,不會(huì)從新渲染
  • computed watcher 和 render watcher 和 dep 和 dep.subs 和 watcher.deps 之間復(fù)雜的關(guān)系
    • computed訪問(wèn)過(guò)程
      • 訪問(wèn)computed,觸發(fā)computed響應(yīng)式的get函數(shù),get函數(shù)中判斷如果dirty=true,那么執(zhí)行computed watchet的evalute方法,即watcher中的get()方法,然后把 Dep.target = computed watcher,執(zhí)行computed getter函數(shù)就是用戶指定的computed函數(shù)
      • 執(zhí)行computed getter函數(shù)的時(shí),因?yàn)橛幸蕾嚨捻憫?yīng)式的data,所以又會(huì)觸發(fā)data的get函數(shù),執(zhí)行dep.depend(),就是把computed watcher中的 newDeps中添加computed依賴項(xiàng)的 dep,同時(shí)向computed依賴項(xiàng)的dep的subs中添加computed watcher,然后又把Dep.target = targetStack數(shù)組中的前一個(gè)watcher,然后返回計(jì)算的結(jié)果,并且把 dirty=false
      • 然后判斷 Dep.taret 存在,就執(zhí)行 computed watcher的depend方法,循環(huán)遍歷computed watcher中的deps,取出dep,執(zhí)行dep.depend
      • 因?yàn)榇藭r(shí)的 Dep.target = render watcher,所以dep.depend會(huì)向render watcher的 newDeps中添加data的dep,向data的dep中的subs中添加render watcher,那么此時(shí)的computed計(jì)算屬性的依賴項(xiàng)的dep中的subs就是[computed watcher, render watcher]這樣就保證了渲染的時(shí)候,computed先于render先執(zhí)行,保證computed有值
    • comuted更新過(guò)程
      • 上面的例子中,change的過(guò)程,是改變了computed的依賴,之后的一系列流程
      • 觸發(fā)computed的響應(yīng)式依賴的dep.notify()方法,循環(huán)遍歷該依賴的dep.subs數(shù)組中的每一個(gè)watcher.update()方法,在上面的例子中subs=[computed watcher, render watcher]所以先執(zhí)行computed watcher
      • compued watcher
        • 在update方法中,會(huì)判斷l(xiāng)azy=true?,因?yàn)閏omputed watcher的lazy=true,所以執(zhí)行 dirty=true
      • render watcher
        • 在update方法中, lazy不為true, 會(huì)執(zhí)行 queueWatcher(this),就是調(diào)用渲染watcher重新求值和渲染

一些單詞

internal:內(nèi)部的

Scheduler:調(diào)度器
queue:隊(duì)列
( flushSchedulerQueue : 刷新調(diào)度器隊(duì)列 )

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了一個(gè) computed watcher
      • 參數(shù)
        • computedWatcherOptions
          • 如何知道是一個(gè)computed watcher => 主要通過(guò)第4個(gè)參數(shù)computedWatcherOptions => { lazy: true },即comoputed watcher的 lazy屬性是true
        • getter
          • 就是用戶自己定義的computed對(duì)象中的函數(shù)
        • noop
          • 是一個(gè)空函數(shù)
      • 注意點(diǎn):
        • 在 new Watcher 計(jì)算屬性watcher的構(gòu)造函數(shù)中
          • this.value=this.lazy ? undefined : this.get()
          • 因?yàn)橛?jì)算屬性watcher的lazy=true,所以不會(huì)立即去執(zhí)行 get() 方法
          • <font color=blue>那什么時(shí)候去執(zhí)行呢get()呢?</font>
            • <font color=blue>執(zhí)行時(shí)機(jī)就是在template中訪問(wèn)了computed</font>,因?yàn)閏omputed又定義了響應(yīng)式,訪問(wèn)了computed屬性就會(huì)執(zhí)行computed的get方法,在get方法中會(huì)執(zhí)行watcher.evaluate()方法,在里面就是去執(zhí)行 get(),從而去計(jì)算computed的結(jié)果
    • <font color=blue size=5>(1-1-2) 把computd定義成響應(yīng)式</font>

      • defineComputed(vm, key, userDef) => Object.defineProperty(target, key, sharedPropertyDefinition) => sharedPropertyDefinition.get => createComputedGetter(key) => computedGetter
      • 也就是說(shuō)訪問(wèn)computed中的 this.xxxx 就會(huì)去執(zhí)行 computedGetter 函數(shù)
    • <font color=blue size=5>(1-1-3) computedGetter - 這個(gè)方法很重要 !!!!!!!!!!!!!!!!!!!!!</font>

      • watcher.evaluate() 執(zhí)行計(jì)算屬性watcher中的evaluate方法
        • 當(dāng)dirty=true,并且watcher存在時(shí),就會(huì)執(zhí)行 computed watcher 的 evalute 方法
        • <font color=blue>evalute</font> 方法會(huì)執(zhí)行 <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 數(shù)組中 push 一個(gè)computed watcher</font>
              • <font color=blue>將 Dep.target 指定為 computed watcher</font>
            • 執(zhí)行用戶在computed對(duì)象中定義的方法,即getter方法newName
              • 比如 computed: {newName() {return this.name + 'new' }}
              • 注意:
                • 在這過(guò)程中又會(huì)觸發(fā) data對(duì)象的影響式,即this.name觸發(fā)響應(yīng)式data中的get函數(shù),因?yàn)樵L問(wèn)了data的name屬性
                • data,computed都有自己的響應(yīng)式
                • 這里data的響應(yīng)式又會(huì)收集計(jì)算屬性的watcher,這個(gè)放在后面的計(jì)算屬性的訪問(wèn)流程中去梳理
                  • 主要就是
                    • 向 computed watcher 的newDeps中添加render watcher的dep
                      - 向 render watcher的依賴的屬性的dep的 subs 中添加 computed watcher
                      - 詳見下文
                      - watcher.depend() 執(zhí)行計(jì)算屬性watcher的depend方法
                      - 放在下面訪問(wèn)流程一起分析
  • 源碼

  • 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 為一個(gè)空對(duì)象

  const isSSR = isServerRendering()
  // 是否是ssr環(huán)境

  for (const key in computed) {
    const userDef = computed[key]
    // userDef 是 computed 的 getter 函數(shù)

    const getter = typeof userDef === 'function' ? userDef : userDef.get
    // getter
      // computed getter 可以是函數(shù) 或者 具有g(shù)et方法的對(duì)象
      // 這里一般都是函數(shù),并且該函數(shù)需要return一個(gè)值

    if (process.env.NODE_ENV !== 'production' && getter == null) {
      warn(
        `Getter is missing for computed property "${key}".`,
        vm
      )
    }
    // 如果不是函數(shù)或者對(duì)象就報(bào)警告


    if (!isSSR) {
      // create internal watcher for the computed property.
      // 非ssr環(huán)境,即瀏覽器環(huán)境,就新建 computed watcher
      // computed watcher
        // computedWatcherOptions = { lazy: true }
        // getter = 用戶自定義的computed對(duì)象中的函數(shù)

      watchers[key] = new Watcher(
        vm,
        getter || noop, // 用戶自定義的computed對(duì)象中的函數(shù),即 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() 過(guò)程中都定義了響應(yīng)式的computed

    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
      // defineComputed 將 computed 變成響應(yīng)式

    } 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 如果在瀏覽器環(huán)境就是 true

  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key) // 定義computed被訪問(wèn)時(shí),觸發(fā)的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)
  // 定義響應(yīng)式 computed
    // 1. 當(dāng)通過(guò) this.xxxx 訪問(wèn)computed,就會(huì)觸發(fā)sharedPropertyDefinition對(duì)象中的get
    // 2. get 其實(shí)就是下面createComputedGetter返回的 computedGetter函數(shù)
}
  • 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]
    // 取出每一個(gè) computed watcher

    if (watcher) {
      if (watcher.dirty) {
        // watcher.dirty
          // 1. 默認(rèn)初始化時(shí),comoputed watcher 的 dirty=true
          // 2. 當(dāng) dirty=true 就會(huì)執(zhí)行 watcher.evaluate()
          // 3. watcher.evaluate() 執(zhí)行完后, dirty=false
          // 總結(jié):  dirty=true => watcher.evaluate() => dirty=false

        watcher.evaluate()
        // watcher.evaluate()
          // 1. 會(huì)去執(zhí)行 computed watcher 中的 get()
            // pushTarget(this)
              // 1. 將 computed watcher 添加到  targetStack 數(shù)組中
              // 2. 將 Dep.target = computed watcher
            // 執(zhí)行 this.getter.call(vm, vm) 即用戶自定義的 computed對(duì)象中的方法
              // 1. 列如: computed: {newName() {return this.name + 'new' }}
              // 2. 因?yàn)椋篶omputed的newName方法中,依賴了data中的this.name,即訪問(wèn)到了this.name就會(huì)觸發(fā)data響應(yīng)式的get方法
              // 3. 所以:ata響應(yīng)式的get方法執(zhí)行過(guò)程如下
                // 獲取到了this.name的值
                // 此時(shí),Dep.target 是computed watcher
                // 然后執(zhí)行this.name對(duì)象的dep類的depend方法進(jìn)行依賴收集
                  // 向 computed watcher 的newDeps中添加render watcher的dep
                  // 向 render watcher 的 subs 中添加 computed watcher
            //  popTarget()
              // 1. targetStack.pop()將 computed watcher從targetStack數(shù)組中刪除
              // 2. 并且將 Dep.target 指定為數(shù)組中的前一個(gè) 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的訪問(wèn)過(guò)程

  • 案例
<!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) 過(guò)程中, 當(dāng)在template模板中使用了 computed 對(duì)象中的key的時(shí)候,就會(huì)觸發(fā)computed響應(yīng)式對(duì)象的 get 方法
  • computed的響應(yīng)式get方法就是computedGetter方法,在該方法中,會(huì)判斷wathcer和watcher.dirty是否存在,存在證明就是computed watcher,就會(huì)執(zhí)行 computed wathcer 的 watcher.evaluate() 方法
  • watcher.evaluate()方法就會(huì)執(zhí)行computed watcher中的get()方法,并將 this.dirty 改為 false
  • watcher.evaluate()
    • get()方法
      • 調(diào)用 <font color=red>pushTarget(this)</font>
        • <font color=red>向 targetStack 數(shù)組中 push 一個(gè)computed watcher</font>
        • <font color=red>將 Dep.target 指定為 computed watcher</font>
      • 然后調(diào)用 computed getter 方法,即用戶自己定義的computed對(duì)象中的方法
        • 比如: computed: {newName() {return this.name + 'new' }}
        • 因?yàn)椋篶omputed的newName方法中,依賴了data中的this.name,即訪問(wèn)到了this.name就會(huì)觸發(fā)data響應(yīng)式的get方法
        • 所以:<font color=DarkOrChid>data響應(yīng)式的get方法執(zhí)行過(guò)程如下:</font>
          • 獲取到了this.name的值
          • 此時(shí),Dep.target 是 computed watcher
            • 然后執(zhí)行this.name對(duì)應(yīng)的dep類的depend方法進(jìn)行依賴收集
              • <font color=DarkOrChid>向 computed watcher 的newDeps中添加render watcher的對(duì)應(yīng)的data屬性的 dep</font>
              • <font color=DarkOrChid>向 render watcher對(duì)應(yīng)的data屬性的dep實(shí)例的 subs 中添加 computed watcher</font>
              • <font color=DarkOrChid>等于說(shuō)data的 this.name 和 computed Watcher 具有同一個(gè) dep 實(shí)例</font>
            • 執(zhí)行完上面的步驟后 dep.subs 和 computed watcher.newDeps 的狀態(tài)是
              • this.name 對(duì)應(yīng)的 dep 實(shí)例中的 subs = [computedWatcher]
              • computed watcher 中的 newDeps = [上面的this.name對(duì)應(yīng)的dep]
          • 返回name的值
      • 調(diào)用 <font color=red>popTarget()</font>
        • <font color=red>targetStack.pop()將 computed watcher從targetStack數(shù)組中刪除</font>
        • <font color=red>并且將 Dep.target 指定為數(shù)組中的前一個(gè) watcher</font>
          • <font color=red>Dep.target = render watcher</font>
          • 在上面的例子中 targetStack 數(shù)組中在執(zhí)行computed 的getter方法時(shí)一共有兩個(gè)成員
          • 第一個(gè):render watcher
          • 第二個(gè):computed watcher
          • pop后還剩一個(gè)render watcher
      • 最后返回computed計(jì)算得到的結(jié)果值
  • watcher.depend()
    • 當(dāng) Dep.target 存在時(shí),才會(huì)執(zhí)行 watcher.depend()
    • 上面執(zhí)行完,<font color=DarkOrChid>Dep.target = render watcher</font>
    • watcher.depend()
      • 然后執(zhí)行 <font color=DarkOrChid>compute watcher 中的 watcher.depend() 方法</font>
      • 然后,<font color=DarkOrChid>從后往前,依次取出 computed watcher 中 deps 數(shù)組中的 dep,執(zhí)行 dep.depend()</font>
        • 注意:上面computed watcher 中的 deps 中的 dep,就是this.name對(duì)象的dep,里面的subs數(shù)組中只有一個(gè)computedWatcher
        • 對(duì)比:在data對(duì)象的屬性被訪問(wèn)的時(shí)候,也會(huì)執(zhí)行data對(duì)應(yīng)的屬性的 dep.depend()
      • <font color=DarkOrChid>Dep.target.addDep(this)</font>
        • <font color=DarkOrChid>此時(shí)的 Dep.targt 是 render watcher</font>,因?yàn)?popTarget() 操作pop出computed watcher后就只剩render watcher了
        • addDep 前
          • render watcher中的 deps 是空數(shù)組
          • render watcher中的 newDeps 是空數(shù)組
        • addDep 主要做兩件事情
          • <font color=DarkOrChid>向 render watcher 的 newDeps 中添加 該render watcher 對(duì)應(yīng)的datad屬性的 dep</font>
          • <font color=DarkOrChid>向 render watcher 對(duì)應(yīng)的data屬性對(duì)應(yīng)的dep類的 subs 中添加 render watcher</font>
          • 添加后,data的dep.subs = [computed watcher, render watcher]
            • <font color=red>這樣當(dāng)this.name屬性修改后觸發(fā)對(duì)應(yīng)的set函數(shù),就會(huì)觸發(fā)dep.notify,然后循環(huán)sub中的watcher,執(zhí)行watcher.update()方法</font>
            • <font color=red>[computed watcher, render watcher]這樣的順序保證了在render的時(shí)候,computed一定有值</font>

(3) computed的更新過(guò)程

  • 上面的例子中,change的過(guò)程,是改變了computed的依賴,之后的一系列流程
  • 觸發(fā)computed的響應(yīng)式依賴的dep.notify()方法,循環(huán)遍歷該依賴的dep.subs數(shù)組中的每一個(gè)watcher.update()方法,在上面的例子中subs=[computed watcher, render watcher]所以先執(zhí)行computed watcher
  • compued watcher
    • 在update方法中,會(huì)判斷l(xiāng)azy=true?,因?yàn)閏omputed watcher的lazy=true,所以執(zhí)行 dirty=true
  • render watcher
    • 在update方法中, lazy不為true, 會(huì)執(zhí)行 queueWatcher(this),就是調(diào)用渲染watcher重新求值和渲染

資料

實(shí)際案例-避坑 https://juejin.im/post/6844903705540558855
詳細(xì) https://juejin.im/post/6844903606676799501
源碼版 https://juejin.im/post/6844903881583886349
computed watcher https://juejin.im/post/6844903648984596494

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容