vue2响应式原理

发布时间:2023-04-11 12:30

首先要知道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 函数执行是异步的,并且在微队列中。

\"\"

 

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号