美文网首页
vue.js设计与实现(阅读)-五(如何代理Object)

vue.js设计与实现(阅读)-五(如何代理Object)

作者: 幸宇 | 来源:发表于2023-01-10 10:55 被阅读0次
理解proxy

vue3是基于proxy代理实现的响应,那么什么事proxy代理,简单的说就是使用proxy可以实现对象的基本语义的代理,基本语义就是对象的基本属性的一些操作,比如读,写,修改,删除等;从而使用proxy对这些操作进行拦截,然而proxy只能对基本的语义进行操作,一些非基本操作,比如函数的调用,我们称之为复合操作,比如obj.fn(),是由两个基本语义组成的,首先读取get到obj.fn这个属性,第二就是函数调用;

理解Reflect

Reflect是一个全局对象,它里面有很多方法比如,get,set,apply等,在proxy拦截器中的方法在Reflect中都能找到,那这个函数的作用是什么?它的功能主要是提供访问对象属性的默认行为,比如以下两个行为是等价的:

obj={foo:1}
//访问属性
console.log(obj.foo) // 1
//Reflect访问
console.log(Reflect.get(obj,'foo')) // 1

既然是等价操作,那么它存在的意义是什么?
主要是Reflect.get可以接收第三个参数receiver,receiver这个参数和我们要实现的相应系统密切相关,receiver可以理解为函数调用中的this,例如上面代码如果填入receiver参数:

console.log(Reflect.get(obj,'foo',{foo:2})) // 输出是2,而不是1

如下代码实现对象的响应代理,对象的读,取,删除,for..in属性操作

const ITERATE_KEY = Symbol()

    let activeEffect;
    const bucket = new WeakMap()
    const effectStack = []
    function effect(fn,options={}){
        const effectFn = ()=>{
            cleanup(effectFn)
            activeEffect = effectFn
            effectStack.push(effectFn)
            let res = fn()
            effectStack.pop()
            activeEffect = effectStack[effectStack.length-1]
            return res
        }
        effectFn.deps=[]
        effectFn.options = options
        if(!effectFn.options.lazy){
            effectFn()
        }
        return effectFn
    }

    function cleanup(effectFn){
        for(let i=0;i<effectFn.deps.length;i++){
            effectFn.deps[i].delete(effectFn)
        }
        effectFn.deps.length = 0;
    }

    function track(target,key){
        if(!activeEffect) return
        let depsMap = bucket.get(target)
        if(!depsMap){bucket.set(target,depsMap=new Map())}
        let deps = depsMap.get(key)
        if(!deps){depsMap.set(key,deps=new Set())}
        deps.add(activeEffect)
        activeEffect.deps.push(deps)
    }

    function trigger(target,key,type){
        let depsMap = bucket.get(target)
        if(!depsMap) return
        let effects = depsMap.get(key)
        const effectsToRun = new Set()
        effects && effects.forEach(effectFn=>{
            if(activeEffect!==effectFn){
                effectsToRun.add(effectFn)
            }
        })

        if(type==='ADD' || type ==='DELETE'){
            const iterateEffects = depsMap.get(ITERATE_KEY)
            // 将与ITERATE_KEY相关联的副作用函数也添加到effectsToRun
            iterateEffects && iterateEffects.forEach(effectFn=>{
                if(activeEffect!==effectFn){
                    effectsToRun.add(effectFn)
                }
            })
        }

        effectsToRun.forEach(effectFn=>{
            if(effectFn.options.scheduler){
                effectFn.options.scheduler()
            }else{
                effectFn()
            }
        })
    }
    function reactive(obj){
        return new Proxy(obj,{
            // has(target,key){
            //     track(target,key)
            //     return Reflect.has(target,key)
            // }
            ownKeys(target){
                track(target,ITERATE_KEY)
                return Reflect.ownKeys(target)
            },
            get(target,key,receiver){
                // 代理对象可以通过raw属性访问原始数据
                if(key==='raw'){
                    return target
                }
                track(target,key)
                return Reflect.get(target,key,receiver)
            },
            set(target,key,newVal,receiver){
                // 先取旧值
                const oldValue = target[key]
                const type = Object.prototype.hasOwnProperty.call(target,key)?'SET':'ADD'
                const res = Reflect.set(target,key,newVal,receiver)
                if(target===receiver.raw){
                    if(oldValue!==newVal&&(oldValue===oldValue||newVal===newVal)){
                        trigger(target,key,type)
                    }

                }
                return res
            },
            deleteProperty(target,key){
                const hadKey = Object.prototype.hasOwnProperty.call(target,key)
                const res = Reflect.deleteProperty(target,key)
                if(hadKey && res){
                    trigger(target,key,'DELETE')
                }
                return res
            }
        })
    }

    const obj = {}
    const proto = {bar:1}
    const child = reactive(obj)
    const parent = reactive(proto)

    // 使用parent作为child的原型
    Object.setPrototypeOf(child,parent)
    effect(()=>{
        // console.log(child)
        console.log(child.bar)
    })
    child.bar = 2

相关文章

  • vue.js设计与实现(阅读)-五(如何代理Object)

    理解proxy vue3是基于proxy代理实现的响应,那么什么事proxy代理,简单的说就是使用proxy可以实...

  • Vue MVVM框架响应式数据原理

    MVVM 是一种设计思想,Vue 框架中 MVVM 是如何实现的呢? Vue.js 是采用 Object.defi...

  • Swift超基础语法(协议与代理篇)

    代理设计模式是开发中比较常见的设计模式之一,那么在Swift中应该如何实现代理与协议呢? 协议(protocol)...

  • 阅读 《vue.js 设计与实现》

    这本书正好适合现在的我阅读,因此每天抽时间倍加感恩的学习,这里简要的记录一下收获。 响应式原理初探 我们可以对 d...

  • 阅读 《vue.js 设计与实现》4.4

    存在问题 - 分支切换 有时候,虽然effect读取了data的某个属性,但实际上,这个读取是没有副作用的。例如,...

  • 阅读 《vue.js 设计与实现》4.5

    嵌套的 effect 与 effect 栈 原先用全局变量 activeEffect 来存储副作用, 内层的 ef...

  • 代理

    来源:java动态代理实现与原理详细分析 代理模式是常用的java设计模式,他的特征是代理类与委托类实现同样的接口...

  • java 动态代理

    动态代理模式介绍 实现原理 设计动态代理类(DynamicProxy)时,不需要显式实现与目标对象类(RealSu...

  • 2020-09-15 技术回顾

    屏幕监测帧率如何实现 A CADisplayLink object is a timer object that ...

  • 反射与代理设计模式

      代理设计模式是在程序开发中使用最多的设计模式,代理设计模式的核心是有真实业务实现类和代理业务实现类,并且代理类...

网友评论

      本文标题:vue.js设计与实现(阅读)-五(如何代理Object)

      本文链接:https://www.haomeiwen.com/subject/orpucdtx.html