一. 如何创建一个响应式对象?
在vue3中通过使用Proxy创建一个对象的代理,从而实现对对象基本操作的拦截
const target = {age:10}
const observed = new Proxy(target,baseHandler)
二. 响应式对象的get和set
2.1 get
当访问observed的某个属性时,会触发get操作
const target = { age: 10 }
const observed = new Proxy(target, {
get(target, key) {
console.log('触发get')
const res = Reflect.get(target, key)
}
})
observed.age
2.2 set
当设置observed的某个属性时,会触发set操作
const target = { age: 10 }
const observed = new Proxy(target, {
get(target, key) {
console.log('触发get')
const res = Reflect.get(target, key)
return res
},
set(target,key,value){
console.log('触发set')
const res = Reflect.set(target, key,value)
return res
}
})
observed.age++
三. 依赖收集与触发依赖
在vue3中当读取响应式对象的某个属性时,会触发Proxy的get操作,此时可在get中收集依赖。当修改某个属性时触发set操作,在此时重新触发依赖
在get中收集依赖,在set中触发依赖
const target = { age: 10 }
const observed = new Proxy(target, {
get(target, key) {
console.log('依赖收集')
track(target,key)
const res = Reflect.get(target, key)
return res
},
set(target,key,value){
console.log('触发依赖')
tigger(target,key)
const res = Reflect.set(target, key,value)
return res
}
})
3.1 存储依赖的容器
定义一个全局的Map。const targetMap = new Map() targetMap的键就是原始对象target,值也是一个Map,这个Map的键是对象的某个属性,值为依赖于此属性的依赖
3.2 依赖
进行依赖收集,首先要定义依赖。我们可以通过定义一个ReactiveEffect类,然后new ReactiveEffect得到一个依赖。
const user = reactive({ age: 10 }) // 创建响应式对象
effect(() => { newAge = user.age + 1 })
function effect(fn){
const _effect = new ReactiveEffect(fn)
_effect.run()
}
class ReactiveEffect{
private _fn: any;
constructor(fn) {
this._fn = fn
}
run() {
activeEffect = this // 在track中收集这个依赖
const result = this._fn() // 执行传入的fn,触发依赖收集
return result
}
}
3.3 track
const targetMap = new Map()
function track(target,key){
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
targetMap.set(target, depsMap)
}
let dep = depsMap.get(key)
if (!dep) {
dep = new Set()
depsMap.set(key, dep)
}
if(dep.has(activeEffect))return // 已经存了就跳过
dep.add(activeEffect)
}
3.4 trigger
function trigger(target,key){
const depsMap = tragetMap.get(target)
const dep = depsMap.get(key)
for(const effect of dep){
effect.run() // 再次执行用户传入的fn
}
}