网上已经有很多文章结合源码叙述了Vue框架中data与computed的实现原理,但大多都是在源代码的基础上进行讲解,对于没有仔细读过源码的同学来说,理解起来会有些费劲。这篇文章会从这两者的渲染以及内部执行过程来阐述内在原理。(注:不会过于分析源代码,想根据源码理解的,请阅读相关的文章)
Object.definePoperty
Object.defineProperty这个相信大家都知道,它是Vue实现响应式的核心方法,也是Vue不兼容ie8及以下浏览器的主要原因。在这个方法里可以定义get、set方法(类似Java),以此来取值和赋值。Vue在初始化的时候,会将普通对象中的数据,用这个方法进行转换。
Watcher
Watcher即观察者,区别于Vue中的watch API,它是底层封装一个类。Vue中的任何操作都是基于数据的,每当render渲染、数据被访问等时候,都会创建Watcher,其主要作用就是观察数据的变化,并根据变化来进行及时的响应。
Dep
Dep即订阅者,它也是底层封装的一个类。Vue在用Object.definePoperty方法转换普通对象数据的时候,都会创建Dep,其作用是,当某个操作运用了数据的时候,就会将这一操作的watcher存储其中,也就是所说的依赖收集。如果数据发生了改变,就会通知所有存过的watcher,让它们进行更新操作。
以上这三者,是Vue响应式的核心,理解它们对理解Vue至关重要。而Vue中的data和computed的实现,也都是应用了它们。
下面分别对data、computed的过程进行简要描述,可以理清它们的实现过程与响应式原理
data
1 | <template> |
根据这个例子,来说下当data被访问或更新的时候,其实现的过程:
1、当初始化的时候,应用Object.defineProperty将data中的数据进行转换,创建Dep订阅者,并设置get、set方法
2、组建创建模板template的时候,会执行render方法,并创建renderWatcher观察者
3、在render方法访问data中的数据msg时,会执行get方法,在get方法中msg进行依赖收集,也就是将renderWatcher存入到Dep中。执行完收集的操作后,将数据返回给render方法,供render渲染。
4、当点击change按钮的时候,会改变msg数据,执行set方法进行数据更改。在set方法中,会让Dep通知所有存储的watcher进行更新。此时存储的是renderWatcher,而renderWatcher接受到通知后,会重新执行渲染方法来渲染组件。
computed
其实computed与data类似,也会讲其中的属性应用Object.defineProperty进行转换,而定义在计算属性的函数,而这个函数会作为这一属性的get方法,举例说明一下:
1 | computed { |
在初始化的时候,会将用Object.defineProperty将newMsg进行转换,同时把上面定义函数作为newMsg的get方法,当然也会在get方法中设置对应的观察者watcher
下面再来根据例子来说明一下,当data中的数据进行访问和更新时,整体的实现过程:
1 | <template> |
初始化的时候,会将data和computed中的数据运用Object.defineProperty进行转换,都创建各自的Dep,data中的数据会设置get、set方法,computed中的函数作为其属性的get方法。
组建创建模板template的时候,会执行render方法,并创建renderWatcher观察者。
在render方法访问到data中的数据时,会执行msg的get方法,在get方法中msg进行依赖收集,将renderWatcher存入msg的Dep中。执行完操作后,将数据返回给render,供其渲染。
在render方法访问到computed中的newMsg时,会执行newMsg的get方法,在get方法中newMsg进行依赖收集,将renderWatcher存入newMsg的Dep中。
newMsg的get方法中,会访问data中的msg,执行msg的get方法,在msg的get方法中msg会进行依赖收集,将newMsgWatcher存入msg的Dep中,并将数据返回给newMsg
newMsg拿到msg的数据后,进行字符串拼接,将结果返回给render方法供其渲染
点击change按钮的时候,会执行msg的set方法设置数据,并让Dep通知所有存储的watcher进行更新。此时的watcher有renderWatcher和newMsgWatcher,renderWatcher会重新渲染template中的msg,而newMsgWatcher会通知computed中的newMsg。
newMsg接收到通知后,会重新拼接字符串,并让其Dep通知所有存储的watcher进行更新。此时的watcher只有renderWatcher,renderWatcher会重新渲染template中的newMsg。
整体渲染过程结束。(Vue当然不会一个一个进行渲染,它在底层做了复杂的逻辑算法,提高整体的性能,此处单独说明是为了叙述清楚,)
通过以上的过程分析,可以明白Vue中的data、computed都是基于前面提到的三者实现的,其核心原理都是一样的,所以理解好这三点,就可以轻松理解Vue响应式的整个过程。