# 7. vue响应式原理
# 1. 初始化处理
初始化数据initData
时调用obsreve
函数。
observe
函数主要做了3件事:
- 如果不是对象则啥都不做退出。
- 对象已经是响应式的了,有
__ob__
了,直接返回这个依赖收集器。 - 对象还不是响应式的,执行
new Observer()
。
Observer
中主要做了3件事:
- 新建一个
new Dep()
依赖收集器,定义到obj.__ob__
上。 - 如果是对数组,修改数组原型,遍历数组,如果不是对象则退出,是对象则继续调用
observe
。 - 如果是对象,则对对象遍历将对象中的属性变为响应式的,调用
defineReactive
defineReactive
主要做了:
- 对对象值val继续执行
observe
get
中进行依赖收集dep.depend()
set
中如果添加了新值则执行observe
set
中进行派发更新dep.notify()
class Observer {
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
//1. 改变数组原型,对push/pop/unshift/shift/reverse/sort/splice方法做出修改,
//2. 对数组执行了这7个方法,则更新视图
//3. 如果执行了push/unshift/splice对数组新增属性,则还要执行observeArray方法,收集依赖。
this.observeArray(value)//遍历数组,执行observe函数
} else {
this.walk(value)//遍历对象对每个值创建依赖收集器收集依赖
}
}
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
export function observe (value) {
if (value !== null && typeof value === 'object') {
return
}
let ob
//如果已经有依赖收集器了(代码省略):直接赋值
ob = value.__ob__
//如果没有依赖收集器(代码省略):新建new Dep()
ob = new Observer(value)
return ob
}
//定义一个响应式属性在对象上
export function defineReactive (obj,key,val) {
const dep = new Dep()
observe(val)
Object.defineProperty(obj, key, {
get: function reactiveGetter () {
dep.depend()
return value
},
set: function reactiveSetter (newVal) {
if (newVal === val) return
val = newVal
observe(newVal)
dep.notify();
}
})
}
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
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
改写数组原型:
const result = original.apply(this, args)//执行原始的数组方法
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2. 收集依赖、更新视图
收集依赖流程:
observe ->
walk ->
defineReactive ->
get ->
dep.depend() ->
watcher.addDep(new Dep()) ->
watcher.newDeps.push(dep) ->
dep.addSub(new Watcher()) ->
dep.subs.push(watcher)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
更新视图流程:
set ->
dep.notify() ->
subs[i].update() ->
watcher.run() || queueWatcher(this) ->
watcher.get() || watcher.cb ->
watcher.getter() ->
vm._update() ->
vm.__patch__()
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Dep.target = null
//Watcher中使用
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
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
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
class Watcher {
vm: Component;
cb: Function;
id: number;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
getter: Function;
value: any;
constructor (
vm: Component,
expOrFn: string | Function,
) {
this.vm = vm
this.id = ++uid // uid for batching
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
//lifecycle.js中mountComponent定义的的new Watcher()中的updateComponent方法,执行vm._update(vm._render(), hydrating),更新视图
this.getter = expOrFn
this.value = this.get()
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
pushTarget(this)//Dep中定义,Dep.target=this
let value
const vm = this.vm
value = this.getter.call(vm, vm)
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)
}
}
}
/**
* Depend on all deps collected by this watcher.
*/
depend () {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}
}
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
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
# 3. 参考
vue.js源码 - 剖析observer,dep,watch三者关系 如何具体的实现数据双向绑定 (opens new window)