其实就是vue.js设计与实现的读书笔记()
前置知识
Proxy
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义。
Proxy.get
get(target, propKey, receiver)
- target -目标对象
- propKey -属性名
- receiver -proxy实例本身
let target={
name:"name",
get alias(){
console.log("----",this)//这个this绑定的就是reflect的receiver
return this.name
}
}
const proxy=new Proxy(target,{
get(target,key,receiver){
console.log(key)
console.log("receiver",receiver)
return Reflect.get(target,key,receiver)
}
})
proxy.alias
Reflect
总的来说就是将Object上面一些方法,转成语言的方法,写得更合理一点。一般与Proxy搭配使用
Reflect.get(target, propertyKey[, receiver])
- target -目标对象
- propKey -属性名
- receiver -如果target对象中指定了getter,receiver则为getter调用时的this值。
实现reactive
初步实现
export function reactive(target:any){
if(!isObject(target)){
return target
}
const proxy=new Proxy(target,{
get(target,key,receiver){
return Reflect.get(target,key,receiver)
},
set(target,key,receiver){
return Reflect.set(target,key,receiver)
},
})
return proxy
}
function isObject(obj:any){
return obj !== null && typeof obj === 'object'
}
但是这样多次代理同一个对象后会多很多不同的代理对象
(vue3文档)为保证访问代理的一致性,对同一个原始对象调用 reactive() 会总是返回同样的代理对象
let obj={}
let reactive1=reactive(obj)
let reactive2=reactive(obj)
console.log(reactive1===reactive2)//false
引入weakMap 减少冗余代理对象
于是考虑到使用哈希表来存储,利用哈希表的特性(key-value唯一) 来查询之前有没有存过就创建新对象,存过直接返回之前存过的对象
使用WeakMap
是因为WeakMap
的键名所指向的对象,不计入垃圾回收机制(标记清除法的时候WeakMap的键指向不算),便于进行垃圾回收
let reactiveMap=new WeakMap()
export function reactive(target:any){
if(!isObject(target)){
return target
}
if(reactiveMap.get(target)){
return reactiveMap.get(target)
}
const proxy=new Proxy(target,{
get(target,key,receiver){
return Reflect.get(target,key,receiver)
},
set(target,key,receiver){
return Reflect.set(target,key,receiver)
},
})
reactiveMap.set(target,proxy)
return proxy
}
function isObject(obj:any){
return obj !== null && typeof obj === 'object'
}
自动解包
(vue3文档) 对一个已存在的代理对象调用 reactive() 会返回其本身
这个是利用了当前对象上有没有Proxy
产生的getter
来判断这个对象是不是已经代理了。
首先是看这个对象的ReactiveFlags
属性,如果这个对象是代理对象的话,它的getter
会被拦截,返回true
,如果没有就返回underfined
const ReactiveFlags="_v_IS_REACTIVE"
export function reactive(target:any){
if(!isObject(target)){
return target
}
if(reactiveMap.get(target)){
return reactiveMap.get(target)
}
if(target[ReactiveFlags]) return target
const proxy=new Proxy(target,{
get(target,key,receiver){
if(key===ReactiveFlags){
return true
}
return Reflect.get(target,key,receiver)
},
set(target,key,receiver){
return Reflect.set(target,key,receiver)
},
})
reactiveMap.set(target,proxy)
return proxy
}
function isObject(obj:any){
return obj !== null && typeof obj === 'object'
}