美文网首页
vue cumputed原理

vue cumputed原理

作者: 安石0 | 来源:发表于2019-08-23 09:39 被阅读0次

    new Vue的时候:执行 initComputed (vm, computed)

    function initComputed (vm, computed) {
    ...
    for (var key in computed) {
        var userDef = computed[key];
        var getter = typeof userDef === 'function' ? userDef : userDef.get;
        if (!isSSR) {
          // create internal watcher for the computed property.
          watchers[key] = new Watcher(
            vm,
            getter || noop, // 这个值就是用户定义的computed: {key: val} 的val
            noop,
            computedWatcherOptions
          );
    }
    ...
    }
    

    所以本质上computed是一个watcher

     constructor (
        vm: Component,
        expOrFn: string | Function,
        cb: Function,
        options?: ?Object,
        isRenderWatcher?: boolean
      ) {
    ...
     if (typeof expOrFn === 'function') {
    // 这里的expOrFn就是用户定义的computed{key: val}中的val
          this.getter = expOrFn
        } 
    ...
    
    }
    

    假设用户computed中定义为

    用户的computed中: {
        fullName () {
         // 'aa' + 'bb'
         return this.firstName + this.lastName
      }
    }
    

    假设这个时候firstName变成了'a' , lastName变成了'abb'
    这两个值的变化都会触发派发更新
    会触发watcher.update,当前的watcher为computed

     update () {
        /* istanbul ignore else */
        if (this.computed) {
          // A computed property watcher has two modes: lazy and activated.
          // It initializes as lazy by default, and only becomes activated when
          // it is depended on by at least one subscriber, which is typically
          // another computed property or a component's render function.
          if (this.dep.subs.length === 0) {
            // 当你并没有使用过这个computed的时候,其实就可以啥都不做
            // In lazy mode, we don't want to perform computations until necessary,
            // so we simply mark the watcher as dirty. The actual computation is
            // performed just-in-time in this.evaluate() when the computed property
            // is accessed.
            this.dirty = true
          } else {
            // In activated mode, we want to proactively perform the computation
            // but only notify our subscribers when the value has indeed changed.
           // 有使用的case
            this.getAndInvoke(() => {
              this.dep.notify()
            })
          }
        } else if (this.sync) {
          this.run()
        } else {
          queueWatcher(this)
        }
      }
    

    上一步分析到:会执行

    this.getAndInvoke(() => {
      this.dep.notify()  // this为当前的wacther
    })
    

    getAndInvoke

    getAndInvoke (cb: Function) {
        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
          this.dirty = false
          if (this.user) {  
          // 我们在new Vue({watch: {}})写的watch
            try {
              cb.call(this.vm, value, oldValue)
            } catch (e) {
              handleError(e, this.vm, `callback for watcher "${this.expression}"`)
            }
          } else {
             // 这里执行上一步的 () => {this.dep.notify()}
            cb.call(this.vm, value, oldValue)
            // 这里会派发更新,像普通的data.fullName值一样,会重新渲染vue
          }
        }
      }
    

    wacther.get

    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
      }
    

    结论:

    computed初始化的时候做了一个get求值,执行了一次用户定义的表达式,这个computed watcher会订阅firstName和lastName的(执行过程中有其他的响应式数据也会订阅),firtName或者lastName变化会触发computed表达式执行,但是当新值和旧值相等时,不会触订阅了fullName的watcher更新。所以会有缓存一说。

    附录1:响应式梳理

    响应式梳理

    相关文章

      网友评论

          本文标题:vue cumputed原理

          本文链接:https://www.haomeiwen.com/subject/zmtzsctx.html