reactive.js实现
// 是否object
const isObject = obj => typeof obj === 'object' && obj !== null
// 使用es6的Proxy模拟vue3响应式
function reactive(obj){
if(!isObject(obj)) return obj
return new Proxy(obj,{
get(target,key,receiver){
const res = Reflect.get(target,key,receiver)
console.log('get',res)
// 依赖收集
track(target,key)
// 递归
return isObject(res)?reactive(res):res
},
set(target,key,value,receiver){
const res = Reflect.set(target,key,value,receiver)
console.log('set',key)
// 触发副作用
trigger(target,key)
return res
},
deleteProperty(target,key){
const res = Reflect.deleteProperty(target,key)
// 触发副作用
trigger(target,key)
console.log('deleteProperty',key)
return res
}
})
}
//临时存储副作用函数
const effectStack = []
// 副作用函数:建立传入fn和其内部的依赖之间映射关系
function effect(fn){
// 执行依赖的get的方法
const e = createReactiveEffect(fn)
// 立即执行
e()
return e
}
function createReactiveEffect(fn){
//处理错误 保存到stack
const effect = function(...args){
try {
// 入栈
effectStack.push(effect)
//执行
return fn(...args)
} finally{
// 出栈
effectStack.pop()
}
}
return effect
}
// 依赖管家
const targetMap = new WeakMap()
// 依赖收集
function track(target,key){
//获取副作用函数
const effect = effectStack[effectStack.length-1]
if(effect){
// 初始化
let depMap = targetMap.get(target)
if(!depMap){
depMap = new Map()
targetMap.set(target,depMap)
}
// 从depMap中获取副作用函数集合
let deps = depMap.get(key)
// 初始化的deps不存在
if(!deps){
deps = new Set()
depMap.set(key,deps)
}
// 放入新传入的副作用
// 使用Set可以去重
deps.add(effect)
}
}
// 出触发副作用
function trigger(target,key){
// 获取 target对应的set
const depMap = targetMap.get(target)
if(!depMap){
return
}
const deps = depMap.get(key)
if(deps){
//循环执行内部所有副作用函数
deps.forEach(dep => dep())
}
}
/*
// 测试effect
const state = reactive({foo:'哈哈哈',nb:{a:'你好'},arr:[112]})
effect(()=>{
console.log('effect',state.foo,state.nb.a)
})
state.foo = '测试1'
*/
//响应式reactive测试
/**
*
const state = reactive({foo:'哈哈哈',nb:{a:'你好'},arr:[112]})
state.foo
state.foo = '呵呵'
state.hi = 'jja'
delete state.hi
state.arr.push(33,888)
state.arr
state.arr.pop()
state.arr
state.nb.a
state.nb.a = '是他'
state.nb.a
state.nb = {b:'换一个他'}
state.nb.b
*/
html测试视图更新
<div id="app"></div>
<script src="./reactive.js"></script>
<script>
const state = reactive({
name:'无敌',
age:10
})
effect(()=>{
app.innerHTML = `我叫${state.name},今年${state.age}了`
})
setTimeout(()=>{
state.name = '改名无双'
},3000)
</script>
|