前言
在一篇文章中简单讲了 vue 是如何把模板解析成 render function 的,这一篇文章就来讲讲 vue 是如何把数据包装成 reactive,从而实现 MDV(Model-Driven-View) 的效果。
先说明一下什么叫 reactive,简单来说,就是将数据包装成一种可观测的类型,当数据产生变更的时候,我们能够感知到。
而 Vue 的相关实现代码全部都在 core/observer
目录下,而要自行阅读的话,建议从 core/instance/index.js
中开始。
在开始讲 reactive 的具体实现之前,先说说几个对象:Watcher、Dep、Observer。
Watcher
Watcher 是 vue 实现的一个用于观测数据的对象,具体实现在 core/observer/watcher.js
中。
这个类主要是用来观察方法/表达式
中引用到的数据(数据需要是 reative 的,即 data 或者 props)变更,当变更后做出相应处理。先看一下 Watcher 这个类的入参:
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: Object
解释一下这几个入参是干嘛的:
- vm:当前这个 watcher 所属的 VueComponent。
- expOrFn:需要监听的 方法/表达式。举个例子:VueComponent 的 render function,或者是 computed 的 getter 方法,再或者是
abc.bbc.aac
这种类型的字符串(由于 vue 的 parsePath 方法是用 split('.') 来做的属性分割,所以不支持abc['bbc']
)。expOrFn 如果是方法,则直接赋值给 watcher 的 getter 属性,如果是表达式,则会转换成方法再给 getter。 - cb:当 getter 中引用到的 data 发生改变的时候,就会触发该回调。
- options:额外参数,可以传入的参数为包括
deep
、user
,lazy
,sync
,这些值默认都是为 false。- deep 如果为 true,会对 getter 返回的对象再做一次深度遍历,进行进一步的依赖收集。
- user 是用于标记这个监听是否由用户通过 $watch 调用的。
- lazy 用于标记 watcher 是否为懒执行,该属性是给 computed data 用的,当 data 中的值更改的时候,不会立即计算 getter 获取新的数值,而是给该 watcher 标记为
dirty
,当该 computed data 被引用的时候才会执行从而返回新的 computed data,从而减少计算量。 - sync 则是表示当 data 中的值更改的时候,watcher 是否同步更新数据,如果是 true,就会立即更新数值,否则在 nextTick 中更新。
了解了入参是用来干嘛的之后,也就基本上知道 Watcher 这个对象干了啥。
Dep
Dep 则是 vue 实现的一个处理依赖关系的对象,具体实现在 core/observer/dep.js
中,代码量相当少,很容易理解。
Dep 主要起到一个纽带的作用,就是连接 reactive data 与 watcher,每一个 reactive data 的创建,都会随着创建一个 dep 实例。参见 observer/index.js 中的defineReactive
方法,精简的 defineReactive 方法如下。
function defineReactive(obj, key, value) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend();
}
return value
}
set(newValue) {
value = newValue;
dep.notify()