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
将来的自己,会感谢现在不放弃的自己!

浙公网安备 33010602011771号