文章参考了霍春阳的《Vue.js设计与实现》,是自己在阅读过程中的一些思考和理解
伪代码
const bocket = new Set()
const data = {
name: 'mean'
}
const proxy = new Proxy(data, {
//重写get函数,当读取这个值时,在处理函数桶中加入修改函数
get(target, key) {
bucket.add(effect)
return target[key]
},
//重写set函数,当修改这个值时,执行桶中所有函数
set(target, key, newValue) {
target[key] = newValue
bocket.forEach(fn => fn())
return true
}
})
//读取数据放在页面
function effect() {
document.body.innerText = proxy.name
}
effect()
//3秒后修改值
setTimeout(() => {
proxy.name = 'newName'
}, 3000)
通过副作用函数,读取代理值。当数据发生改变时,在set函数中再次调用副作用函数,再重新读一遍修改后的值,放在原本的位置,达到响应式的功能。
完善一点的响应式
//全局变量
let activeEffect
//静态数据
const data = {
name: 'mean'
}
//存储所有数据的函数,用到了WeakMap的特性
//这里的映射为bocket--->WeakMap<target, Map()>
const bocket = new WeakMap()
const proxy = new Proxy(data, {
get(target, proxy) {
if(!activeEffect) return
//从桶中获取当前对象的Map,因为bocket中可以存储对个对象
let depsMap = bocket.get(target)
//如果没有则立即创建一个,并放在bocket中
//这里映射为 target--->Map<key,Set()>
if(!depsMap) {
bocket.set(target, (depsMap = new Map()))
}
//从对象的众多key中拿到我们要修改的key的Set()集合
//如果没有则立即创建,并加入到depsMap中
//这里的映射为key--->Set(fn,fn,fn....)
let deps = depsMap.get(proxy)
if(!deps) {
depsMap.set(proxy, (deps = new Set()))
}
//将我们传入的修改函数加入到对应key的Set()集合中
deps.add(activeEffect)
//返回读取的值
return target[proxy]
},
set(target, proxy, value, reciver) {
//原对象中修改要变的值
target[proxy] = value
//获取到target的Map,没有则返回
const depsMap = bocket.get(target)
if(!depsMap) return
//拿到要修改的key的Set集合()
const effects = depsMap.get(proxy)
//依次执行集合中的函数
effects && effects.forEach(fn => fn())
return true
}
})
//暴露的effect函数,透给用户使用,用于对数据进行修改和调用
function effect(fn) {
activeEffect = fn
activeEffect()
}
effect(() => {
document.body.innerText = proxy.name
})
//2秒后修改数据
setTimeout(() => {
proxy.name = 'fiht'
}, 2000);
最后可以把get和set中间的操作部分分割出来,方便管理
function track(target, proxy) {
if(!activeEffect) return
let depsMap = bocket.get(target)
if(!depsMap) {
bocket.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(proxy)
if(!deps) {
depsMap.set(proxy, (deps = new Set()))
}
deps.add(activeEffect)
}
function trigger(target, proxy) {
const depsMap = bocket.get(target)
if(!depsMap) return
const effects = depsMap.get(proxy)
effects && effects.forEach(fn => fn())
}