乐趣区

关于vue.js:vue源码watcher阅读记录

1. 首先从 computed 钻研开始

function initComputed (vm: Component, computed: Object) {
  // 首先应用连等形式,申明 watchers,并给 vue 实例上增加_computedWatchers 属性,二者指向同一对象,用来记录所有的 computed
  const watchers = vm._computedWatchers = Object.create(null)
  // 判断是否是服务端渲染
  const isSSR = isServerRendering()
  for (const key in computed) {const userDef = computed[key]
    // computed 两种申明形式函数、对象:{get,set}
    const getter = typeof userDef === 'function' ? userDef : userDef.get
    if (!isSSR) {
      // create internal watcher for the computed property.
      // 给每一个 computed 创立 Watcher 实例,并增加到 watchers,vm._computedWatchers 也会增加
      watchers[key] = new Watcher(
        vm,
        getter || noop,
        noop,
        computedWatcherOptions
      )
    }
    // 判断 vm 上是否已存在雷同属性名,存在报错,不存在代理到 vm 上,以便 this.xxx 调用
    if (!(key in vm)) {defineComputed(vm, key, userDef)
    } else if (process.env.NODE_ENV !== 'production') {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)
      } else if (vm.$options.methods && key in vm.$options.methods) {warn(`The computed property "${key}" is already defined as a method.`, vm)
      }
    }
  }
}

2.defineComputed
1) 首先看下 noop 函数是什么?

// noop 就是一个空函数
export function noop (a?: any, b?: any, c?: any) {}

2) 看一下 createComputedGetter 函数

function createComputedGetter (key) {// 例: computedName(){return '我的名字是' + this.name}
  // 返回一个 getter 函数,将 computed 属性 (computedName) 的 get 指向到 watcher.value,
  // 这里的 watcher.value 会触发 watcher.prototype.get 办法,进而触发 computed 中依赖响应式变量的 get 办法,// 而后触发 dep.depend(), 将 watcher 增加到该变量闭包中 dep 实例的 subs 数组
  return function computedGetter () {const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      // computed 的默认会传 lazy:true, watcher.dirty 初始值等于 lazy,所以这里是 true
      if (watcher.dirty) {//this.value 获取值, 并且把 dirty 设置为 false,这样就能够起到缓存的作用,屡次拜访同一个 computed,只会触发一次 watcher.get()
        // evaluate () {//   this.value = this.get()
        //   this.dirty = false
        // }
        watcher.evaluate() // 翻译是估值,求函数的值}
      if (Dep.target) {watcher.depend()
      }
      return watcher.value
    }
  }
}

3) 最初看一下最终的 defineComputed 函数,这里会用到 noop 和 createComputedGetter,所以在下面提前理解一下

// 例: computedName(){return '我的名字是' + this.name}
export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  // 判断是否是服务端渲染,这里只钻研客户端渲染的状况
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {// 客户端渲染时 shouldCache 为 true, 也就是会将 computedName.get 设置为 createComputedGetter(),
    // createComputedGetter 会返回一个 getter 办法,见下方
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : createGetterInvoker(userDef)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  // 如果 computed 属性是 function 时,被设置时会报错,这就是咱们平时为什么 computed 不能被设置的起因
  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
      )
    }
  }
  // 将 computed 属性 (computedName) 定义到 vm 上,这里的 target 就是 vm(vue 实例)
  Object.defineProperty(target, key, sharedPropertyDefinition)
}
退出移动版