vue3 的响应式原理
做响应式:
// 使用
let vdata = reactive(data)
vue2、vue3 的响应式区别:
1、vue2 (动态添加、删除属性 不行)
2、数组
3、初始化深层递归,速度会慢
4、新增的数据结构不支持 map、set
关于 proxy类的使用
先理解下述函数如何执行
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
const val = Reflect.get(obj, key)
return typeof val === 'object' ? reactive(val) : val
},
set(target, key, val) {
// console.log(`set: ${key}`)
const ret = Reflect.set(target, key, val)
return ret
},
deleteProperty(target, key) {
// console.log(`delete: ${key}`)
return Reflect.deleteProperty(target, key)
},
})
}
const state = reactive({
msg: 'hello world',
obj: { msg: 10 },
})
state.msg
state.foo = 'hello hui'
delete state.msg
console.log(state.msg)
console.log(state.obj.msg)
// get: msg
// set: foo
// delete: msg
// get: msg
// undefined
// get: obj
// get: msg
// 10
关于 reactive 代码实现
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
const val = Reflect.get(target, key)
track(target, key)
return typeof val === 'object' ? reactive(val) : val
},
set(target, key, val) {
// console.log(`set: ${key}`)
// 出发trigger
const ret = Reflect.set(target, key, val)
trigger(target, key)
return ret
},
deleteProperty(target, key) {
// console.log(`delete: ${key}`)
trigger(target, key)
return Reflect.deleteProperty(target, key)
},
})
}
const effectStack = []
//映射表
const targetMap = new WeakMap()
function effect(fn) {
const e = createReactiveEffect(fn)
// 立即执行一次 处理错误 放入effectStack
e()
return e
}
function createReactiveEffect(fn) {
const effect = function () {
try {
effectStack.push(effect) // 放入
return fn() // 执行
} finally {
effectStack.pop()
}
}
return effect
}
// 依赖收集 weekmap (对象映射 获取 map) map(key值 映射 set) set 是 收集的是effect事件
function track(target, key) {
// 尝试获取effect函数
const effect = effectStack[effectStack.length - 1] // 取最后一个
if (effect) {
let depMap = targetMap.get(target) // 获取map
if (!depMap) {
// 如果不存在, 第一次收集
depMap = new Map()
targetMap.set(target, depMap) // 不存在就塞进 targetMap
}
let deps = depMap.get(key) // 获取set
if (!deps) {
deps = new Set()
depMap.set(key, deps)
}
deps.add(effect) // 收集依赖
}
}
// 依赖触发
function trigger(target, key) {
let depMap = targetMap.get(target) // 获取map
if (depMap) {
let deps = depMap.get(key)
deps.forEach((fn) => {
fn()
})
}
}
demo 测试:
const state = reactive({
msg: 'hello world',
obj: { msg: 10 },
})
effect(() => {
state.msg
console.log(state.msg)
})
state.msg = 'hello boy'
html 测试:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app">
<div id="title"></div>
</div>
<script src="./index.js"></script>
<script>
const data = reactive({ title: 'vue3 响应式' })
const dom = document.getElementById('title')
effect(() => {
dom.textContent = data.title
})
setInterval(() => {
data.title = new Date().toString()
}, 1000)
</script>
</body>
</html>
runtime-code 模块了解 watch、computed 实现