响应式原理
vue2
首先需要了解三个概念和发布-订阅模式(data 属性发布者,setter 事务处理,watcher 订阅者)
- Observer(数据的劫持者,用来劫持 data、props、computed 的数据)
- Deps(依赖对象,每个对象属性都会有一个依赖对象,用来收集该属性的观察者)
- Watcher(观察者,每个组件都会有一个,绑定的是组件更新)
具体流程
- 在组件实例初始化时,Observer 会使用 object.definePropty 对 data 这些对象遍历它的所有属性做 getter 和 setter 做数据劫持,同时也会创建一个观察者对象。
- 在 getter 里会将每个属性和一个依赖对象做关联
- 在首次渲染时,会去获取 data 里面的数据,触发这个属性的 getter 方法,然后 Deps 将这个观察者加入进其观察者列表里。
- 当属性修改,则触发了 setter 方法,依赖对象触发 notice 方法,遍历观察者列表触发更新,观察者接到依赖对象的通知,会触发其 update 方法,做组件级更新执行 render,diff 对比更新 vdom。
- 因为数组的长度是不定的,过长去遍历劫持是很耗性能的,所以针对数组,Observe 并不是使用和对象一样的方式劫持。
- 内部如果判断属性值是数组,会自定义 push,pop,shift,unshift,slice,sort,reverse 方法,方法内部会调用数组原型方法和触发依赖通知,然后使用 Object.defineProty 将其劫持,再将这个数组原型上的方法替换为自定义方法。
- 在首次渲染时,如果访问的时 data 里的数组时,会检查当前的观察者对象,然后将其加入到数组的 Deps 对象的列表里,当触发了自定义的数组方法,则会观察者会去执行更新。
- 因为 object.definePropty 只有 getter/setter,无法监听到对象属性的增删,数组也无法监听到项的修改和 length 的修改,所以提供了$set 方法去触发视图更新。
vue3
和 vue2 不同它需要先了解两个概念
- reactive (内部做数据劫持)
- effect (副作用函数,内部包含渲染方法)
具体流程
- 初始化实例时,会使用 reactive 将对象和集合使用 proxy,做 getter/setter/deletePropty 这些操作,针对对象和集合会有不同的处理方法。
- 然后会执行 effect 的方法,传入渲染方法,会在内部有执行逻辑,最后会把自身返回出去,作为实例的更新方法。
- effect 内部不仅会执行渲染方法,也会设置一些当前 effect 的属性和设置当前的活动 effect,以便在 getter 时做依赖收集
- 执行渲染方法,会去获取数据,触发 getter,使用 track 方法做依赖收集。
- track 方法内部,会根据当前的传入的对象,在实例 Map 集合查找有没有以改对象为 key 的属性,没就创建一个 Set 集合。然后在这个 Set 集合里查找有没有传入 key 的 effect,没就将当前活动的 effect 加入到该 Set 里。
- 当修改数据值,触发 setter,使用 trigger 方法做更新,trigger 内部首先会查找当前对象里有没有这个属性,有就查找 Map 集合里的 Set 集合里的 effect 方法,然后执行。没有就执行新增方法,做依赖的收集。
- 渲染方法里会 patch,diff 这些,最后渲染页面
JStar