vue响应式原理

Wenn 于 2022-10-26 发布

监听对象的操作

const obj = {
    name: 'xi',
    age:20
}
Object.keys(obj).forEach(key=>{
    let value = obj[key]
    Object.defineProperty(obj,key,{
        get:function(){
            console.log(`${key}属性被访问`)
            return value
        }
        set:function(newValue){
        console.log(`${key}属性被设置值`)
        value = newValue
    }
    })
})

无法监听更多的操作,比如删除,新增

Proxy 代理 函数

const obj = {
    name:'xi',
    age:20
}
//设置obj的代理对象,第一个参数为目标对象,第二个参数为重写捕获器Trap,如果不重写则传入一个空对象
const objProxy = new Proxy(obj,{
    //三个参数,第一个目标对象,二参数目标属性,三参数代理对象
    get:function (target,key){
        console.log('对象属性被访问')
        return target[key]
    }
    set:function(target,key,newValue){
    console.log('对象属性被设置值')
    target[key] = newValue
}
})  

Proxy有13种操作捕获器

const obj = {
    name:'xi',
    age:20
}
const objProxy = new Proxy(obj,{
//监听in操作
    has:function(target,key){
        return key in target
    }
//监听delete操作
    deleteProperty:function(target,key){
    delete target[key]
}
}) 

Reflect 反射 对象

const obj = {
    name:'xi',
    age:20
}
const objProxy = new Proxy(obj,{
	get:function(target,key){
        Reflect.get(target,key)
    }
    set:function(target,key,newValue){
    	Reflect.set(target,key,newValue)  
    //操作成功与否会返回一个布尔值,可以根据布尔值来进行其他操作
}
}) 

Receiver

const obj = {
    _name:'xi',
    get name(){
        return this._name
    }
    set name(newValue){
        this._name = newValue
    }
}
const objProxy = new Proxy(obj,{
    get:function(target,key,recevier){
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    return Reflect.set(target,key,newValue,recevier)  
    //传入recevier时会改写obj中的this一次,此时Reflect执行时访问的是objProxy对象
    //首先访问objProxy,之后在访问obj对象本身,共访问两次
}
})

响应式

当对象中的参数值进行修改时,可以自动再次执行某段代码

class Depend {
    constructor(){
        this.reactiveFns = []
    }
    
    addDepend(reactiveFn){
        this.reactiveFns.push(reactiveFn)
    }
    
    notify(){  //当改变之后每次都调用一次这个方法
        this.reactiveFns.forEach(fn=>{
            fn()
        })
    }
}

const depend = new Depend()
function watchFn(fn){ //设置一个函数来判断哪些函数需要是响应式
    depend.addDepend(fn)
}
watchFn(function(){
    console.log(objProxy.name)  //缺点为仅监听了name属性的变化
})
watchFn(function(){
    console.log(objProxy.age)
})
const obj = {
    name:'xi',
    age:20
}
const objProxy = new Proxy(obj,{
    get:function(target,key,recevier){
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    Reflect.set(target,key,newValue,recevier)
    depend.notify()   //此时只要设置了代码这个就会自动执行一次
}
})
优化
const targetMap = new WeakMap()
function getDepend(target,key){
    //根据target对象获取map
    let map = targetMap.get(target)
    if(!map){
        map = new Map()
        targetMap.set(target,map)
    }
    //根据key获取depend对象
    let depend = map.get(key)
    if(!depend){
        depend = new Depend()
        map.set(key,depend)
    }
    return depend
}

const objProxy = new Proxy(obj,{
    get:function(target,key,recevier){
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    Reflect.set(target,key,newValue,recevier)
    const depend = getDepend(target,key)
    depend.notify()   //此时只要设置了代码这个就会自动执行一次
}
})
优化二-收集依赖
let activeReactiveFn = null
function watchFn(fn){ //设置一个函数来判断哪些函数需要是响应式
    activeReactiveFn = fn
    //默认添加时会执行一次,执行会调用Proxy对象,所以会在get中执行一次,添加对应的依赖
    fn()
    activeReactiveFn = null
}
//监听对象的属性变量:Proxy(Vue3)/Object.defineProperty(Vue2)
const objProxy = new Proxy(obj,{
    get:function(target,key,recevier){
        //根据target,key获取对应的depend
        const depend = getDepend(target,key)
        //给depend对象添加响应函数
        depend.addDepend(activeReactiveFn)
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    Reflect.set(target,key,newValue,recevier)
    const depend = getDepend(target,key)
    depend.notify()   //此时只要设置了代码这个就会自动执行一次
}
})
优化三
class Depend {
    constructor(){
        this.reactiveFns = new Set() //去重。存在一个fn之后不会再进行
    }
    
    depend(){
        if(activeReactiveFn){
            this.reactiveFns.add(activeReactiveFn) 
        }
    }
    
    notify(){  //当改变之后每次都调用一次这个方法
        this.reactiveFns.forEach(fn=>{
            fn()
        })
    }
}

const objProxy = new Proxy(obj,{
    get:function(target,key,recevier){
        //根据target,key获取对应的depend
        const depend = getDepend(target,key)
        //给depend对象添加响应函数
        depend.depend()
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    Reflect.set(target,key,newValue,recevier)
    const depend = getDepend(target,key)
    depend.notify()   //此时只要设置了代码这个就会自动执行一次
}
})

优化四 对所有对象进行

function reactive(obj){
    return new Proxy(obj,{
    get:function(target,key,recevier){
        //根据target,key获取对应的depend
        const depend = getDepend(target,key)
        //给depend对象添加响应函数
        depend.depend()
        return Reflect.get(target,key,recevier)
    }
    set:function(target,key,newValue,recevier){
    Reflect.set(target,key,newValue,recevier)
    const depend = getDepend(target,key)
    depend.notify()   //此时只要设置了代码这个就会自动执行一次
}
})
}

//之后所有的对象都开始使用reactive'创建一个代理对象
const foo={name:'k',age:30}
const fooProxy = reactive(foo)

Vue2响应式原理

function reactive(obj){
    Object.keys(obj).forEach(key=>{
        let value = obj[key]
        Object.defineProperty(obj,key,{
            get:function(){
                const depend = getDepend(obj,key)
                depend.depend()
                return value
            }
            set:function(newValue){
            value = newValue
            const depend = getDepend(obj,key)
            depend.notify()
        }
        })
    })
}