想对 Vue 有一个较深的了解,就必须要深入内核,看清楚当我们数据发生变化或触发动作时,Vue 是如何帮助我们更新视图的。
首先让我们来看一下“栗子”:
模板:
1
2
3<div id="app" @click="changeMsg">
{{ message }}
</div>脚本:
1
2
3
4
5
6
7
8
9
10
11var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
},
methods: {
changeMsg() {
this.message = 'Hello World!'
}
}
})
这里我们可以看到,当我们触发 changeMsg
事件时,模板中的 message
会被更新,并且我们的视图也会随之更新,那么这里发生了什么呢?
我们来看一下下面这张图:
下面分两个部分去了解其中的“玄机”:
几个核心概念
- 监听器 Observer ,用来劫持并监听所有属性(转变成setter/getter形式),如果属性发生变化,就通知订阅者
- 订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理
- 订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图
- 解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化
核心流程
- vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
- 在初始化时调用 defineReactive,实例化一个 dep 负责派发更新,使用 Object.defineProperty 中的 setter 负责依赖收集
- Dep:负责管理 Watcher,dep.notify() 实际调用 watcher 中的 update,按照需要更新的队列依次更新(按 id 先后顺序)
- 调用 watcher.before() 触发 beforeUpdate 的 hook,最终通过 vm._update 更新视图(到 diff 流程中)
- 实现一个数据监听器 Observer,对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
- (实现一个指令解析器 Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数)
- 实现一个 Watcher,作为连接Observer和Compile的桥梁,订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图