首先要知道vue2 是2013年 基于 ES5开发出来的 

我们常说的重渲染就是重新运行render函数

vue2响应式原理简单来说就是vue官网上的这图片

 

通过 Object.defineProperty 遍历对象的每一个属性,把每一个属性变成一个 getter 和 setter 函数,读取属性的时候调用 getter,给属性赋值的时候就会调用 setter.

当运行 render 函数的时候,发现用到了响应式数据,这时候就会运行 getter 函数,然后 watcher(发布订阅)就会记录下来。当响应式数据发生变化的时候,就会调用 setter 函数,watcher 就会再记录下来这次的变化,然后通知 render 函数,数据发生了变化,然后就会重新运行 render 函数,重新生成虚拟 dom 树。

深入了解:

我们要明白,响应式的最终目标:是当对象本身或对象属性发生变化时,会运行一些函数,最常见的就是 render 函数。不是只有 render,只要数据发生了变化后运行了一些函数,就是响应式,比如 watch。

在具体实现上,vue 采用了几个核心部件:

1.Observer

2.Dep

3.Watcher

4.Scheduler

Observer

observer 要实现的目标非常简单,就是把一个普通的对象转换成响应式的对象

为了实现这一点,observer 把对象的每个属性通过 object.defineProperty 转换为带有 getter 和 setter 的属性,这样一来,当访问或者设置属性时,vue 就会有机会做一些别的事情。

在组件的生命周期中,这件事发生在 beforeCreate 之后,create 之前。

具体实现上,他会递归遍历对象的所有属性,以完成深度的属性转换。

但是由于遍历只能遍历到对象的当前属性,无法监测到将来动态添加或者删除的属性,因此 vue 提供了$set和$delete 两个实例方法,但是 vue 并不提倡这样使用,我讲到 dep 的时候我再说为什么。

对于数组的话,vue 会更改它的隐式原型,之所以这样做是因为 vue 需要监听那些可能改变数组内容的方法。

数组 --> vue 自定义的对象 --> Array.prototype

总之,observer 的目标,就是要让一个对象,它属性的读取,赋值,内部数组的变化都要能够被 vue 感知到。

Dep

这里有两个问题没解决,就是读取属性时要做什么事,属性变化时又要做什么事,这个问题就得需要 dep 来解决

dep 的含义是 dependency 表示依赖的意思。

vue 会为响应式对象中的每一个属性,对象本身,数组本身创建一个 dep 实例,每个 dep 实例都可以做两件事情:

1,记录依赖:是谁在用我  

2,派发更新:我变了,我要通知那些用我的人

当读取响应式对象的某一个属性时,他会进行依赖收集,有人用到了我

当改变某个属性时,他会派发更新,那些用我的人听好了,我变了

为什么尽量不要使用$set $delete ?  

因为如果模板上没有用到值的话,你凭空加了一个数据,理论上来说应该不会重新运行render函数,但是上一级的dep发现自身发生改变了,所以也会导致重新运行render函数。

所以vue不建议使用$set 和$delete,最好提前先写上数据,哪怕先给数据赋值为 null;

watcher

这里又出现了一个问题,就是 dep 如何知道是谁在用我呢

watcher 就解决了这个问题

当函数执行的过程中,用到了响应式数据,响应式数据是无法知道是谁在用自己的

所以,我们不要直接执行函数,而是把函数交给一个 watcher 的东西去执行,watch 是一个对象,每个函数执行时都应该创建一个 watcher,通过 wacher 去执行

watcher 会创建一个全局变量,让全局变量记录当前负责执行的 watcher 等于自己,然后再去执行函数,在函数执行的过程中,如果发生了依赖记录,那么 dep 就会把这个全局变量记录下来,表示有一个 wathcer 用到了我这个属性。

当 dep 进行派发更行时,他会通知之前记录的所有 watcher,我变了。

Scheduler

现在还剩下最后一个问题啊,就是 dep 通知 watcher 之后,如果 wathcer 执行重新运行对应的函数,就有可能导致频繁运行,从而导致效率低下,试想,如果一个交给 watcher 的函数,它里面用到了属性 a,b,c,d,那么 a,b,c,d 都会记录依赖,然后这四个值都以此重新赋值,那么就会触发四次更新,这样显然不行啊,所以当 watcher 收到派发更新的通知后,实际上并不是立即执行,而是通过一个叫做 nextTick 的工具方法,把这些需要执行的 watcher 放到事件循环的微队列,nextTick 是通过 Promise then 来完成的。

也就是说,在响应式数据发生变化时,render 函数执行是异步的,并且在微队列中。

 

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐