vue2与vue3响应式系统模拟实现
VUE2
/***
* 收集所有依赖
*/
class Dep {
constructor() {
// 集合Set(元素不重复)
this.subscriber = new Set()
}
// addEffect(effect) {
// this.subscriber.add(effect)
// }
depend() {
if (activeEffect) {
this.subscriber.add(activeEffect)
}
}
notify() {
this.subscriber.forEach(effect => {
effect()
})
}
}
let activeEffect = null
function watchEffect(effect) {
// dep.addEffect(effect)
activeEffect = effect
// 默认第一次立即执行一次
effect()
activeEffect = null
}
/**
* Map(key: value): key是一个字符串
* WeakMap(key(对象): value): key是一个对象,弱引用
*/
/**
* 弱引用:
*/
const targetMap = new WeakMap()
/**
* 根据target、key返回dep
*/
function getDep(target, key) {
// 1.根据对象(target)取出对应的Map对象
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
// 2.取出具体的dep对象
let dep = depsMap.get(key)
if (!dep) {
dep = new Dep()
depsMap.set(key, dep)
}
return dep
}
/**
* 数据劫持
* vue2
*/
function reactive(raw) {
Object.keys(raw).forEach(key => {
// const dep = new Dep() // 每次创建新的,找不到相关的依赖
const dep = getDep(raw, key)
let value = raw[key]
/**
* 为什么不直接操作raw[key]
* 当使用raw[key]去调用/修改,会触发get/set,当外界调用/修改,又使用raw[key],会循环
*/
Object.defineProperty(raw, key, {
get() {
dep.depend()
return value
},
set(newValue) {
if (value != newValue) {
value = newValue
// 当值发生改变,通知改变
dep.notify()
}
}
})
})
return raw
}
// 测试代码
// const info = { counter: 100, name: 'why' }
// const user = { height: 1.88 }
const info = reactive({ counter: 100, name: 'why' })
const user = reactive({ height: 1.88 })
const dep = new Dep()
watchEffect(() => {
console.log(info.counter * 2, info.name);
})
watchEffect(() => {
console.log(info.counter * info.counter);
})
watchEffect(() => {
console.log(info.counter * 10, info.name);
})
watchEffect(() => {
console.log(user.height);
})
/**
* 不同的依赖修改变化,通知对应的依赖进行修改
* dep1[info.counter] subscribers
* dep2[info.name] subscribers
* dep2[user.height] subscribers
* 用数据结构对其进行管理
* const targetMap = new Map()
* targetMap[info] = new Map(info)
* infoMap[counter] = dep1.subscribers
* infoMap[name] = dep2.subscribers
* targetMap[user] = new Map(user)
* userMap[height] = dep3.subscribers
*/
info.counter++
dep.notify()
info.name = 'lilei'
vue3
/***
* 收集所有依赖
*/
class Dep {
constructor() {
// 集合Set(元素不重复)
this.subscriber = new Set()
}
// addEffect(effect) {
// this.subscriber.add(effect)
// }
depend() {
if (activeEffect) {
this.subscriber.add(activeEffect)
}
}
notify() {
this.subscriber.forEach(effect => {
effect()
})
}
}
let activeEffect = null
function watchEffect(effect) {
// dep.addEffect(effect)
activeEffect = effect
// 默认第一次立即执行一次
effect()
activeEffect = null
}
/**
* Map(key: value): key是一个字符串
* WeakMap(key(对象): value): key是一个对象,弱引用
*/
/**
* 弱引用:
*/
const targetMap = new WeakMap()
/**
* 根据target、key返回dep
*/
function getDep(target, key) {
// 1.根据对象(target)取出对应的Map对象
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
// 2.取出具体的dep对象
let dep = depsMap.get(key)
if (!dep) {
dep = new Dep()
depsMap.set(key, dep)
}
return dep
}
/**
* 数据劫持
* vue3
*/
function reactive(raw) {
return new Proxy(raw, {
get(target, key) {
const dep = getDep(target, key)
dep.depend()
return target[key]
/**
* 为什么这里可以直接操作target[key]?
* 因为只有操作代理的时候,才会进入该步骤,所以操作原始对象的时候不会有问题
*/
},
set(target, key, newValue) {
console.log('key', newValue)
const dep = getDep(target, key)
target[key] = newValue
dep.notify()
}
})
}
// 测试代码
// const info = { counter: 100, name: 'why' }
// const user = { height: 1.88 }
// const info = reactive({ counter: 100, name: 'why' })
const user = reactive({ height: 1.88 })
const dep = new Dep()
// watchEffect(() => {
// console.log(info.counter * 2, info.name);
// })
// watchEffect(() => {
// console.log(info.counter * info.counter);
// })
// watchEffect(() => {
// console.log(info.counter * 10, info.name);
// })
// watchEffect(() => {
// console.log(user.height);
// })
/**
* 不同的依赖修改变化,通知对应的依赖进行修改
* dep1[info.counter] subscribers
* dep2[info.name] subscribers
* dep2[user.height] subscribers
* 用数据结构对其进行管理
* const targetMap = new Map()
* targetMap[info] = new Map(info)
* infoMap[counter] = dep1.subscribers
* infoMap[name] = dep2.subscribers
* targetMap[user] = new Map(user)
* userMap[height] = dep3.subscribers
*/
// info.counter++
// dep.notify()
// info.name = 'lilei'
为什么vue2中不能直接使用raw[key]直接去做get/set,而vue3中可以?
- 修改对象的不同
- 使用
defineProperty时,修改原来的obj对象就可以触发拦截 - 使用
proxy,就必须修改代理对象,即Proxy的实例才可以触发拦截
- 使用