Vue 的 _init() 方法调用了 initState 方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14core\instance\state.js
export function initState (vm: Component) {
  vm._watchers = []
  const opts = vm.$options
  // code... 省略代码
  // 处理computed
  if (opts.computed) initComputed(vm, opts.computed)
  // 处理watch
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch)
  }
}
先看watch:
Watch
| 1 | core\instance\state.js | 
createWatcher 会调用 vm.$watch(expOrFn, handler, options)
vm.$watch 是在 core\instance\index.js 调用 stateMixin(Vue) 时定义的
| 1 | Vue.prototype.$watch = function ( | 
Vue.prototype.$watch 中实例化了一个 Watcher,返回了一个 unwatchFn 函数,调用它可以卸载该 watch。
接下来我们转到 Watcher ,这一块的代码有些多,我会省略大部分代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102core\observer\watcher.js
export default class Watcher {
  // code... 省略代码
  constructor (vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean) {
    this.vm = vm
    // 如果是渲染的 Watcher ,vm._watcher 指向当前 this
    if (isRenderWatcher) {
      vm._watcher = this
    }
    // 向 vm._watchers push this
    vm._watchers.push(this)
    
    // 这是 watch 的配置项
    if (options) {
      this.deep = !!options.deep
      this.user = !!options.user
      this.lazy = !!options.lazy
      this.sync = !!options.sync
      this.before = options.before
    } else {
      // watch 没配置项,给默认配置
      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() // 调用 this.get()
  }
  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
    // 👇 pushTarget(this),将 Dep.target 赋值为 this
    pushTarget(this)
    let value
    const vm = this.vm
    try {
      // 要 watch 的获取值
      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)
      }
      // 👇 清空 Dep.target
      popTarget()
      // 清空依赖
      this.cleanupDeps()
    }
    return value
  }
  // 👇 以下代码全部省略
  addDep (dep: Dep) {} // 向此指令添加依赖项
  cleanupDeps () {} // 清理依赖集合
  update () {} // 在依赖变更时调用
  run () {}
  evaluate () {}
  depend () {}
  
  teardown () {} // 移除 watch
}
这里调用 pushTarget(this),将 Dep.target 赋值为 this ,这样做的目的是可以将 当前 Watcher 添加到 Dep 的订阅列表中
我们回过头看 defineReactive 函数的其中一段:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// 使用 Object.defineProperty 对数据进行监听
Object.defineProperty(obj, key, {
  enumerable: true, // 可枚举
  configurable: true, // 可配置
  get: function reactiveGetter () {
    const value = getter ? getter.call(obj) : val
    // 这个Dep.target是用于添加 Watcher 的,正常情况下它的值为 null
    if (Dep.target) { 👈👈👈👈👈👈👈
      // 添加 Watcher 相关
      dep.depend()
      if (childOb) {
        childOb.dep.depend()
        if (Array.isArray(value)) {
          dependArray(value)
        }
      }
    }
    // 返回对应值
    return value
  },
  set: function reactiveSetter (newVal) {
    // code... 代码省略
  }
})
通过 defineReactive 函数,我们劫持了数据,Watcher 的 get 方法会去获取劫持过的数据,获取数据就会触发 get,
上面手指所指的地方,当 Dep 存在静态属性 target 的时候,就成功的向 Dep 添加了 Watcher,一旦数据改变,就会触发用户定义好的回调。
接着看 computed:
Computed
第一部分代码
| 1 | core\instance\state.js | 
这里以一个最简单的例子来说明:1
2
3
4
5computed: {
  fullName: function () {
    return this.firstName + this.lastName
  }
}
首先:const getter = typeof userDef === ‘function’ ? userDef : userDef.get
getter 其实就是我们定义的 computed 函数:1
2
3function () {
  return this.firstName + this.lastName
}
然后看标注一的位置,这里 new 了一个 Watcher1
2
3
4
5
6watchers[key] = new Watcher(
  vm,
  getter || noop,
  noop,
  computedWatcherOptions // { lazy: true }
)
这里只 new Watcher(),Watcher 内部的方法都不会被调用。
之后调用了 defineComputed 方法,我们接着看第二部分代码:
第二部分代码
| 1 | const sharedPropertyDefinition = { | 
我们看标注二,这里我们默认只考虑浏览器环境,所以:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// 假设 key = 'fullName'
sharedPropertyDefinition.get = createComputedGetter(fullName)]
// => 等同于
sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers['fullName']
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        ------------------------------------ 标注四 ------------------------------------
        watcher.depend()
      }
      return watcher.value
    }
  },
  set: noop
}
// 然后使用 Object.defineProperty 对 fullName 进行劫持
Object.defineProperty(target, key, sharedPropertyDefinition)
当我们使用 computed 时,就触发了他的 get 方法,然后会调用前面定义的 Watcher 的 evaluate 方法。evaluate 方法会调用 Watcher 的 get,然后我们转到 Watcher 中看 get 方法:
| 1 | core\observer\watcher.js | 
get 方法首先将 Dep.target 指向当前 Watcher
接着标注三的位置调用了 this.getter.call(vm, vm),也就是 return this.firstName + this.lastName。
然后触发了 firstName 与 lastName 的 getter,将当前 Watcher 添加到 这两个 data 的订阅者列表中,如果 firstName 与 lastName 发生变化都会调用当前 Watcher 的 update 方法。同时也算出了 value 值。
Watcher 的 update 方法被调用,就会触发 get 方法,获取最新的值,算出最新的 value。
最后 return watcher.value
END
TIPS: 这里如果不清楚如何订阅者列表可以去看这篇

