Vue 响应式原理

一、前言

vue 响应式原理是数据渲染到视图,及数据修改触发视图更新的过程。

二、原理

1、数据监听

当把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 为这些属性设置 getter/setter

这些 getter/setter 对用户来说不可见,但是在内部它们让 Vue 能够追踪依赖,在属性被访问和修改时通知变更。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function defineReactive(obj: Object, key: string, ...) {
var dep = new Dep()

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// ....
dep.depend()
return value
// ....
},
set: function reactiveSetter (newVal) {
// ...
val = newVal
dep.notify()
// ...
}
})
}

2、依赖收集

每个组件在渲染的过程中都会创建一个对应的 watcher(订阅者) 实例,创建 watcher 实例时会去进行模版解析,会触发属性的 getter,而每个属性在最开始设置 setter/getter 时会建立一个与之对应的收集器, getter 的调用会让该收集器记录当前的 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
export default class Watcher {
constructor (vm: Component, expOrFn: string | Function, cb: Function, ...) {
this.cb = cb
this.getter = expOrFn
this.value = this.get()
}

get () {
Dep.target = target
value = this.getter.call(this.vm, this.vm) // 渲染
Dep.target = null
return value
}

addDep (dep: Dep) {
dep.addSub(this)
}

update () {
this.run()
}

run () {
const oldValue = this.value
const value = this.get()
this.cb.call(this.vm, value, oldValue)
}
}
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
// 收集器
export default 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 () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update() // // 调用订阅者的update方法,通知变化
}
}
}
}

3、派发更新

当属性被修改时,会触发属性的 setter ,然后该属性的收集器会遍历当前的所有 watcher 实例并发送通知,watcher 实例会去执行自己的 update() 方法,触发组件的重新渲染,再次去触发属性的 getter。

参考链接

https://cn.vuejs.org/v2/guide/reactivity.html

微信打赏