数据响应原理
简单简述运行过程
1、首先是传入原始对象,使用Observer,通过其getter和setter方法把数据变成响应式数据;
2、在设置和获取数据时,在进行渲染时(执行render函数时)会产生一个watcher,用来收集谁用到了数据和谁改变了数据(收集的数与派发更新的数据这里统称为Dep),
3、然后再把自己收集到的数据给Scheduler(调度器),调度器会把watcher给的每个数据添加到队列中去
4、然后内部会把这些依次添加到nextTick队列中去(微队列中去);等同步代码执行完后就会去执行这些异步队列中的代码
5、执行完后watcher就会继续收集谁用到了数据和谁改变了数据,然后依次循环
分布理解其原理
Observer Observer的目标,就是要让一个对象,它属性的读取、赋值,内部数组的变化都要能够被vue感知到
Observer要实现的目标非常简单,就是把一个普通的对象转换为响应式的对象
如何转换的,就是使用Object的defineProperty的getter和setter访问器属性,去循环遍历对象或者数组,
但这样做也有缺陷,他不能监听到对象的属性添加属性和删除属性,所以vue就在实例中添加了$set方法用来添加和修改,用$delete方法来删除属性
对于数组的添加与删除也是监听不到的,所有vue就重写了数组的 push, prop, shift, unshift, sort, splice, reverse的方法在新的原型链
加入这样方法,反制其污染Array原有的方法,然后把重写的方法挂载到数组的__proto__上这样就变成响应式数组了
具体代码如何实现可看https://blog.csdn.net/weixin_41277748/article/details/111470806
Dep
对于Observer的只是读取与设置,那么读取时要做什么和设置时要做什么,这个问题就需要依靠Dep来解决。 Dep的含义是Dependency,表示依赖的意思。
Vue会为响应式对象中的每个属性、对象本身、数组本身创建一个Dep实例,每个Dep实例都有能力做以下两件事
-
记录依赖(dep.depend()):是谁在用我,谁在读取数,
-
派发数据更新(dep.notify()):我变了,我要通知那些用到我的人,谁设置了我
- 当读取响应式对象的某个属性时,它会进行依赖收集:有人用到了我
- 当改变某个属性时,它会派发更新:那些用我的人听好了,我变了
总的来说就是记录谁干了什么,但是这里有个问题,dep他不知道是谁,他只是收集数据,想要知道是谁就得用Watcher
Watcher 用来监听是谁在做事情
当某个函数执行的过程中,用到了响应式数据,响应式数据是无法知道是哪个函数在用自己的
因此,vue通过一种巧妙的办法来解决这个问题
我们不要直接执行函数,而是把函数交给一个叫做watcher的东西去执行,watcher是一个对象,每个这样的函数执行时都应该创建一个watcher,通过watcher去执行
watcher会设置一个全局变量,让全局变量记录当前负责执行的watcher等于自己,然后再去执行函数,在函数的执行过程中,如果发生了依赖记录dep.depend(),那么Dep就会把这个全局变量记录下来,表示:有一个watcher用到了我这个属性
当Dep进行派发更新时,它会通知之前记录的所有watcher:我变了
每一个vue组件实例,都至少对应一个watcher,该watcher中记录了该组件的render函数。
watcher首先会把render函数运行一次以收集依赖,于是那些在render中用到的响应式数据就会记录这个watcher。
当数据变化时,dep就会通知该watcher,而watcher将重新运行render函数,从而让界面重新渲染同时重新记录当前的依赖。
Scheduler
现在还剩下最后一个问题,就是Dep通知watcher之后,如果watcher执行重运行对应的函数,就有可能导致函数频繁运行,从而导致效率低下
试想,如果一个交给watcher的函数,它里面用到了属性a、b、c、d,那么a、b、c、d属性都会记录依赖,于是下面的代码将触发4次更新:
-
state.a =
"new data"
;
-
state.b =
"new data"
;
-
state.c =
"new data"
;
-
state.d =
"new data"
;
这样显然是不合适的,因此,watcher收到派发更新的通知后,实际上不是立即执行对应函数,而是把自己交给一个叫调度器的东西
调度器维护一个执行队列,该队列同一个watcher仅会存在一次,队列中的watcher不是立即执行,它会通过一个叫做nextTick的工具方法,把这些需要执行的watcher放入到事件循环的微队列中,nextTick的具体做法是通过Promise完成的
nextTick 通过 this.$nextTick 暴露给开发者
也就是说,当响应式数据变化时,render函数的执行是异步的,并且在微队列中
转载:https://blog.csdn.net/weixin_41277748/article/details/115680611