一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。
手写 mini-Vue3的响应式 reactive 下
书接上一回手写 mini-Vue3的响应式 reactive,接下来我们来实现响应式原理的依赖收集,我们分别来实现 effect && track
effect
1. 初始化
这里我们先把effect
函数进行定义,并且接收一个参数 callback
,还需要执行一次该函数
export function effect(callback) {
callback()
}
这里执行 callback
是为了 访问响应式对象属性,去收集依赖
2. 存储callback
在收集依赖的过程中,我们需要将 callback
存储起来
let activeEffect = null
export function effect(callback) {
activeEffect = callback // 访问响应式对象属性,去收集依赖
callback()
activeEffect = null
}
- 为什么还需要重新设置
activeEffect = null
? - 因为收集依赖的时候,如果有嵌套属性的话,是一个递归的过程
track
1.初始化
track
函数接收两个参数
- 目标对象 traget
- 要跟踪的属性 key
export function track(target, key) {}
2. 存储target
这里我们需要将 target 存储到 targetMap 中
let targetMap = new WeakMap()
export function track(target, key) {
if(!activeEffect) return
let depsMap = targetMap.get(target)
if(!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if(!dep) {
depsMap.set(key, (dep = new Set()))
}
dep.add(activeEffect)
}
收集依赖
这里我们只需要找到我们之前编写的 reactive 函数中的 get 加入如下的模块就好
...
get(target, key, receiver) {
// 收集依赖
track(target, key)
},
...
trigger函数
1.初始化
trigger
函数接收两个参数
- traget
- key
export function trigger(target, key) {
}
2.我们需要根据 target 从 targetMap 中 找到 depsMap
export function trigger(target, key) {
// 我们需要根据 target 从 targetMap 中 找到 depsMap
const depsMap = targetMap.get(target)
if(!depsMap) return
// 根据key找对应的 dep集合
const dep = depsMap.get(key)
// dep集合中存储的是 对应key存储的 effect 函数
if(dep){
dep.forEach(effect => {
effect()
});
}
}
3. 修改触发更新函数
set(target, key, value, receiver) {
trigger(target, key)
},
ok,这里就完成的差不多了,对了这里推荐一个测试工具LiveServer
测试
<!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>
<script type="module">
import { reactive, effect } from './reactivity/index.js'
const product = reactive({
name: 'iPhone',
price: 5000,
count: 3
})
let total = 0
effect(() => {
total = product.price * product.count
})
console.log(total)
product.price = 4000
console.log(total)
product.count = 1
console.log(total)
</script>
</body>
</html>
预期:
- 15000
- 12000
- 4000
没有问题!