Vue3.0响应式原理实现—(reactive、effect)
Opened this issue · 0 comments
Believel commented
// Vue3.0 响应式原理
// WeakMap:类似于Map,但是键值只能是对象,键值不会存放在内存中,所以不能用forEach遍历,不会在成内存泄漏
let toProxy = new WeakMap(); // 弱引用映射表,原对象:代理过的对象
let toRaw = new WeakMap(); // 被代理过的对象:原对象
// 判断是否是对象
function isObject(obj) {
return (typeof obj === 'object') && obj !== null
}
// 判断对象中有没有指定的属性
function hasOwn(target, key) {
return target.hasOwnProperty(key)
}
function reactive(target) {
return createReactiveObject(target)
}
function createReactiveObject(target) {
// 不是对象的话,直接返回就行
if (!isObject(target)) return target
let proxy = toProxy.get(target); // 如果已经代理过了,就将代理过的结果返回
if (proxy) {
return proxy
}
if (toRaw.get(target)) { // 防止代理过的对象再次代理
return target
}
let handler = {
get (target, key, receiver) {
console.log('获取')
let result = Reflect.get(target, key, receiver)
// 收集依赖 订阅 把当前的key 和这个effect 对应起来
track(target, key) // 如果目标上的 这个key变化了 重新让数组中的effect执行即可
return isObject(result) ? reactive(result): result
},
set (target, key, value, receiver) {
// 识别是新增属性还是修改属性
const hadKey = hasOwn(target, key) // 判断属性之前有没有
const oldValue = target[key] // 之前的值
let res = Reflect.set(target, key, value, receiver)
// console.log(key)
if (!hadKey) {
// console.log('新增属性')
trigger(target, 'add', key)
} else if (oldValue !== value){ // 这里表示属性已经修改过了
// console.log('修改属性')
trigger(target, 'set', key)
} // 屏蔽无用的修改(这里是修改length长度,其实值已经更改过了)
// 返回一个 bool 值
return res
},
deleteProperty (target, key) {
let res = Reflect.deleteProperty(target, key)
console.log('删除')
return res
}
}
let observed = new Proxy(target, handler)
toProxy.set(target, observed)
toRaw.set(observed, target)
return observed
}
// !代理对象
// let proxy = reactive({ name: { n: 'zf'}, age: 100})
// proxy.name.n
// delete proxy.age
// !如何实现对象本身或者代理对象多次代理
// let p1 = reactive(proxy)
// reactive(proxy)
// reactive(proxy)
// console.log(p1 === proxy)
// !代理数组
// let proxy = reactive([3, 5, 8])
// proxy.push(4)
// proxy[0] = 1
// proxy.length = 100
// 响应式
let activeEffectStacks = [];
// {
// target: {
// key: [fn, fn]
// }
// }
let targetsMap = new WeakMap();
function track (target, key) {
let effect = activeEffectStacks[activeEffectStacks.length - 1]
if (effect) { // 有对应关系 才创建关联
let depsMap = targetsMap.get(target)
if (!depsMap) {
targetsMap.set(target, depsMap = new Map());
}
let deps = targetsMap.get(key)
if (!deps) {
depsMap.set(key, deps = new Set())
}
if (!deps.has(effect)) {
deps.add(effect)
}
// 动态创建依赖关系
}
}
function trigger (target, type, key) {
let depsMap = targetsMap.get(target)
if (depsMap) {
let deps = depsMap.get(key)
if (deps) {
deps.forEach(effect => {
effect()
})
}
}
}
function effect(fn) {
// 需要把fn这个函数变成响应式的函数
let effect = createReactiveEffect(fn)
effect(); // 默认先执行一次
}
function createReactiveEffect(fn) {
// 这个就是创建的响应式的effect
let effect = function() {
// run函数的作用:1.让fn执行,2.把effect存到栈中
return run(effect, fn)
}
return effect
}
function run(effect, fn) {
try {
activeEffectStacks.push(effect)
fn()
} finally {
activeEffectStacks.pop()
}
}
// !依赖收集 发布订阅
let obj = reactive({ name: 'ww'})
effect(() => { // effect默认会执行两次,默认先执行一次 之后依赖的数据变化了 会再次执行
// console.log('更新视图')
console.log(obj.name) // 会调用get方法
})
obj.name = 'qq'