vue3响应式原理(小满zs vue3 笔记八)
tip: 带着问题去理解响应式原理why,what,how
一. 响应式原理核心点是什么?
** 数据截持 ** ** 依赖收集 ** ** 派发更新 **
二.vue2的响应式原理? vue3响应式原理?区别是什么?
1. 下面vue2是官网上的这图片
** 通过 Object.defineProperty 遍历对象的每一个属性,把每一个属性变成一个 getter 和 setter 函数,读取属性的时候调用 getter, 给属性赋值的时候就会调用 setter.** ** 当运行 render 函数的时候,发现用到了响应式数据,这时候就会运行 getter 函数,然后 watcher(发布订阅)就会记录下来。 当响应式数据发生变化的时候,就会调用 setter 函数,watcher 就会再记录下来这次的变化,然后通知 render 函数, 数据发生了变化,然后就会重新运行 render 函数,重新生成虚拟 dom 树。 **
下面是具体的调度图
render -> watcher ($get 收集依赖, $set方法 派发更新), 执行Scheduler 调度队列里数据
vue2响应式原理总结: 1. 数据截持, vue2是通过Object.defineProperty来将对象的每一个属性转化成get,set; 其中修改对象的属性时就会触发set,使用对象的属性时就会触发get; 2. 依赖收集, 就是在渲染视图时,将watcher和具体的属性,通过发布订阅者模式管理,这样数据改变之后就能更精准的更新视图; 3. 派发更新, 它就是通过dep来执行watcher的notify方法. ** 使用Object.defineProperty做响应式的缺点 ** 1. 深度监听,需要一次性递归到底,计算量比较大; 2. 描述符只有get和set,无法监听新增属性和删除属性的操作; 3. 无法原生监听数组 这三个缺点中,第二点是defineProperty本身API的缺陷,而第一点和第三点都是出于性能考虑而做的取舍; ** 解决缺点的方法 ** 当我们通过数组的方法去更改数组时或是直接删除data数据,数据并不能实现响应式,因为Object.defineProperty是没有办法处理属性删除和新增的 因此vue2的响应式,通过数组方法(prop,push),或是删除,vue是不能监听的 vue2中通过vue2中可以通过vue.Delete和vue.Set这些vue内置api来改变属性,实现响应式。
2.0的不足
对象只能劫持 设置好的数据,新增的数据需要Vue.Set(xxx) 数组只能操作,修改某一项值无法劫持。数组的length修改也无法劫持
2. vue3 响应式原理(需要通过tsc把.ts变成js,目前index.html无内容待更新)
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> </div> <script type="module"> import { reactive } from './reactive.js' import { effect } from './effet.js' const user = reactive({ name: "小满", age: 18 }) effect(() => { document.querySelector('#app').innerText = `${user.name} - ${user.age}` }) setTimeout(()=>{ user.name = '大满很吊' setTimeout(()=>{ user.age = '23' },1000) },2000) </script> </body> </html>
reactive.ts
import { track, trigger } from './effet' // 递归实现reactive const isObject = (target) => target != null && typeof target == 'object' // Vue3 的响应式原理依赖了 Proxy 这个核心 API,通过 Proxy 可以劫持对象的某些操作。 export const reactive = <T extends object>(target: T) => { // 第一个参数target return new Proxy(target, { // 第二个参数拦截器, target当前对象,key对象属性,reactive也是当前对象 get(target, key, receiver) { // Reflect保证上下文正确取值 const res = Reflect.get(target, key, receiver) as object // 收集依赖 track(target, key) if (isObject(res)) { return reactive(res) } return res }, // Reflect.set返回的是布尔值 set(target, key, value, receiver) { const res = Reflect.set(target, key, value, receiver) trigger(target, key) return res } }) }
effet.ts
// effect track trigger, 实现effect 副作用函数 // 使用一个全局变量 active 收集当前副作用函数,并且初始化的时候调用一下 let activeEffect: any; export const effect = (fn:Function) => { const _effect = function () { activeEffect = _effect; fn() } _effect() } // 实现track,收集依赖 const targetMap = new WeakMap() export const track = (target: any,key: any) =>{ let depsMap = targetMap.get(target) if(!depsMap){// 第一次无值,对应value是一个Map depsMap = new Map() targetMap.set(target,depsMap) } // 对象取value let deps = depsMap.get(key) if(!deps){ deps = new Set() depsMap.set(key,deps) } // 收集副作用依赖 deps.add(activeEffect) } // 触发依赖 export const trigger = (target: any,key: any) => { const depsMap = targetMap.get(target) const deps = depsMap.get(key) deps.forEach(effect=>effect()) }
参考文章 :
vue2
https://blog.csdn.net/m0_54581080/article/details/126036155
https://blog.csdn.net/weixin_71718397/article/details/125859350
vue3 是blibli小满视频vue3及csdn
将来的自己,会感谢现在不放弃的自己!