vue2响应式原理
概述
响应式数据的目的,就是当数据、对象属性、数组某项发生改变时,能够自发的运行某些函数。在 vue
中最常见的就是数据发生改变时重新运行 render
函数刷新页面。
vue 中实现响应式主要依靠一下几个核心部分
- Observer
- Dep
- Watcher
- Scheduler
Observer
Observer 要实现的功能很简单,就是将一个普通的数据变为响应式数据
- 对于对象:是采用递归调用 Object.defineProperty 来对对象进行劫持,为每个属性添加 getter 与 setter,这样在请求属性或者更改时 vue 能够收到通知来做一些事情。但即使是这样我们还是无法监听到对象属性的增加与删减,因此 vue 提供了
$set
和$delete
实例方法,来对响应式数据进行属性的增减 - 对于数组,vue 会更改数组的隐式原型,将其变为 vue 自定义的对象,对象中对原先一些能改变数组的方法进行了重写,这样 vue 就能监听到数组内容的变化,最后将将自定义对象的隐式原型指向 Array.prototype
Dep
到这里数据已经变为响应式数据了但我们根本不知道读取属性时、修改属性时该做什么事情。而 Dep 就是来解决这个事情的 vue 会为每个响应式数据生成一个 Dep 实例,Dep 实例会做两件事
- 记录依赖:当有人访问这个数据时,把它记录下来
- 派发更新:当数据被修改时通知所有的依赖数据更改了
Wather
现在我们知道当访问一个响应数据时,Dep
会纪录依赖,但 Dep
该怎么知道到底是谁访问了我呢
Watcher
就是用来解决这个问题的
当某个函数运行时用到了响应数据,Dep
是没办法知道到底哪个函数在使用自己
因此我们将这个函数交给 Watcher
来执行。Watcher
会设置一个全局的变量来记录当前执行的 Watcher
,当发生 Dep
记录依赖时,Dep
就将这个全局变量记录下来,表示当前这个 Watcher
用到了自己。当数据改变 Dep
派发更新时就会通知 Watcher
我变了,Watcher
就会重新调用对应的函数
每一个 vue 实例都至少有一个 Watcher
实例来保存 render 函数,当 vue 组件首次渲染时 Watcher
就会调用 render 函数来收集依赖
所有在 render 中用到的响应式数据都会纪录这个 Watcher
当数据发生改变时,Dep
会通知 Watcher
,Wather 就会重新运行 render 函数,更新依赖。
Scheduler
此时 vue 响应式已经基本完成,但仍然存在一些问题。
当 render 中用到了许多响应式数据,如果这些响应式数据都发生了变化,那么记录 render 的 Watcher 会被重复调用多次,浪费了许多性能。这显然是不合理的。
实际上 Watcher 并不会立即执行而是会把自己交给 Scheduler ,Scheduler 会维护一个队列,队列中同一个 Watcher 只能存在一次,vue 内部提供了 nextTick 方法,可以把这些 watcher 放入到事件循环中的微队列中,所以 Watcher 的执行都是异步的
详细流程图
转载自:https://juejin.cn/post/7007795725920632869