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 了一个 Watcher
1
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: 这里如果不清楚如何订阅者列表可以去看这篇