})
// 回调函数立即执行一次,内部使用到的数据更新时会重新执行回调函数
effect(() => {
countEl.innerHTML = `count is ${state.count}, double is ${double.value}, man's name is ${state.man.name}`
})
// 修改响应式数据触发更新
btnEl.addEventListener('click', () => {
state.count++
}, false)
</script>
</body>
</html>
通过示例可以看到实现 Vue3 这个数据响应式需要有 reactive、computed、effect 这几个函数,下面仍然通过从变化侦测及依赖收集两个方面介绍,简单实现这几个函数。
变化侦测
示例中的 reactive 函数是对数据进行响应式化的,因此该函数的功能就类似于 Vue2 中的 defineReactive 函数的 getter/setter 处理,处理后能够对数据的获取及修改操作进行捕获。
const toProxy = new WeakMap()
const toRaw = new WeakMap()const baseHandler = {
get(target, key) {
console.log('Get', target, key)
const res = Reflect.get(target, key)
// 递归寻找
return typeof res == 'object' ? reactive(res) : res
},
set(target, key, val) {
console.log('Set', target, key, val)
const res = Reflect.set(target, key, val)
return res
}
}
function reactive(target) {
console.log('reactive', target)
// 查询缓存
let observed = toProxy.get(target)
if (observed) {
return observed
}
if (toRaw.get(target)) {
return target
}
observed = new Proxy(target, baseHandler)
// 设置缓存
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
reactive 中使用 Proxy 对目标进行代理,代理的行为是 baseHander ,然后对目标对象及代理后的对象进行缓存,防止多次代理。
baseHandler 中就是对数据的获取及修改进行拦截,并通过 Reflect 执行 get/set 的原本操作,并在获取值为 Object 时递归进行响应式处理。很简单地就完成了数据的响应式处理。
依赖收集
依赖收集与 Vue2 类似,在 getter 中收集依赖,setter 中触发依赖,修改 baseHandler 如下:
const baseHandler = {
get(target, key) {
const res = Reflect.get(target, key)
// 收集依赖
track(target, key)
return typeof res == 'object' ? reactive(res) : res
},
set(target, key, val) {
const info = {
oldValue: target[key],
newValue: val
}
const res = Reflect.set(target, key, val)
// 触发更新
trigger(target, key, info)
return res
}
}
track 函数收集依赖,trigger 函数触发依赖更新。
首先需要两个全局变量,用于保存当前待收集的依赖对象的 effectStack 及一个记录所有数据及其对应依赖的表 targetMap 。










