手写实现基础的reactive


github地址: https://github.com/feddiyao/Frontend-05-Template/tree/master/Week%2005

proxy的基本用法

看一下MDN对proxy的定义:Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。
语法:

const p = new Proxy(target, handler)

所以根据proxy的特性,我们可以看到,使用了proxy,我们代码的预期性会变差。
看一下proxy的基本用法:

let object = {
    a:1,
    b:2
}

let po = new Proxy(object, {
    set(obj, prop, val){
        console.log(obj, prop, val);
    }
})

通过对object包装proxy,我们可以通过po.a = 3调用其中的set方法。
看下调用po.x = 5的打印结果:
在这里插入图片描述

模仿reactive实现原理

vue中的reactive也是通过proxy来实现的,我们就把reactive的实现作为一个练习。
首先是实现基础的get和set方法:

let object = {
    a:1,
    b:2
}

let po = reactive(object)


function reactive(object) {
    return new Proxy(object, {
        set(obj, prop, val){
            obj[prop] = val;
            console.log(obj, prop, val);
            return obj[prop];
        },
        get(obj, prop) {
            console.log(obj, prop);
            return obj[prop];
        }
    })
}

此时我们调用po.x = 99页面打印结果如下:
在这里插入图片描述
然后是监听set导致的元素内容变化,我们看下基础实现,定义一个effect,它可以直接监听po上的属性,以代替事件监听的机制。在effdct中进行callback函数的入栈,看下代码:

let callbacks = []

let object = {
    a:1,
    b:2
}

let po = reactive(object)

effect(()=> {
    console.log(po.a)
})

function effect(callback) {
    callbacks.push(callback)
}

function reactive(object) {
    return new Proxy(object, {
        set(obj, prop, val){
            obj[prop] = val;
            console.log(obj, prop, val);
            for (let callback of callbacks) {
                callback();
            }
            return obj[prop];
        },
        get(obj, prop) {
            console.log(obj, prop);
            return obj[prop];
        }
    })
}

po.x = 5

看下控制台的打印结果:
在这里插入图片描述
接下来我们来看一下,如何查看一个函数实际用了哪些变量,我们可以在reactive变量的get方法中获得一个监听的效果。
先对effect方法做一个基本的修改:

    let callbacks = [];

    let useReactivities = [];

    let object = {
        a:1,
        b:2
    }

    let po = reactive(object)

    effect(()=> {
        console.log(po.a)
    })

    function effect(callback) {
        //callbacks.push(callback)
        useReactivities = [];
        callback();
        console.log(useReactivities);
    }

    function reactive(object) {
        return new Proxy(object, {
            set(obj, prop, val){
                obj[prop] = val;
                console.log(obj, prop, val);
                for (let callback of callbacks) {
                    callback();
                }
                return obj[prop];
            },
            get(obj, prop) {
                useReactivities.push([obj, prop]);
                return obj[prop];
            }
        })
    }

看下运行效果图:
在这里插入图片描述
下面补全effect代码:

    let callbacks = new Map();

    let useReactivities = [];

    let object = {
        a:1,
        b:2
    }

    let po = reactive(object)

    effect(()=> {
        console.log(po.a)
    })

    function effect(callback) {
        //callbacks.push(callback)
        useReactivities = [];
        callback();
        console.log(useReactivities);

        for (let reactivity of useReactivities) {
            if (!callbacks.has(reactivity[0])) {
                callbacks.set(reactivity[0], new Map())
            }

            if (!callbacks.get(reactivity[0]).has(reactivity[1])) {
                callbacks.get(reactivity[0]).set(reactivity[1], [])
            }

            callbacks.get(reactivity[0]).get(reactivity[1]).push(callback);
        }

    }

    function reactive(object) {
        return new Proxy(object, {
            set(obj, prop, val){
                obj[prop] = val;
                
                if (callbacks.get(obj) && callbacks.get(obj).get(prop))
                    for (let callback of callbacks.get(obj).get(prop)) {
                        callback();
                    }
                return obj[prop];
            },
            get(obj, prop) {
                useReactivities.push([obj, prop]);
                return obj[prop];
            }
        })
    }

此时当我们运行po.a = 1会触发effect效果,但是 po.b=1就不会触发effect,达到了我们想要实现的监听effect函数引用变量的效果。
但是现在这个函数还是没有那么完美,试i想一下如果object当中的a为一个对象,那么我们在effect中调用po.a.b显然是监听不到的,我们需要继续进行优化。
更新reactive的方法:

 function reactive(object) {
     if (reactivities.has(object))
         return reactivities.get(object);

     let proxy = new Proxy(object, {
         set(obj, prop, val){
             obj[prop] = val;
             
             if (callbacks.get(obj) && callbacks.get(obj).get(prop))
                 for (let callback of callbacks.get(obj).get(prop)) {
                     callback();
                 }
             return obj[prop];
         },
         get(obj, prop) {
             useReactivities.push([obj, prop]);
             if(typeof obj[prop] === "object")
                 return reactivity(obj[prop])
             return obj[prop];
         }
     })

     reactivities.set(object, proxy);

     return proxy;
 }

此时我们调用po.a.b是能够监听到变化的,那么reactive的主要实现逻辑就在上面了。

reactive基础上实现调色盘case

我们在刚才实现的reactive基础上实现一个调色盘的case,看下总的代码:

<input id ="r" type="range" min=0 max=255/>
<input id ="g" type="range" min=0 max=255/>
<input id ="b" type="range" min=0 max=255/>
<div id="color" style="width:100px;height:100px;">

</div>
<script>
    let callbacks = new Map();

    let reactivities = new Map();

    let useReactivities = [];

    let object = {
       r: 1,
       g: 1,
       b: 1
    }

    let po = reactive(object)

    effect(()=> {
        document.getElementById("r").value = po.r;
    })
    effect(()=> {
        document.getElementById("g").value = po.g;
    })
    effect(()=> {
        document.getElementById("b").value = po.b;
    })

    document.getElementById("r").addEventListener("input", event => po.r = event.target.value);

    document.getElementById("g").addEventListener("input", event => po.g = event.target.value);

    document.getElementById("b").addEventListener("input", event => po.b = event.target.value);

    effect(()=> {
        document.getElementById("color").style.backgroundColor = `rgb(${po.r}, ${po.g}, ${po.b})`;
    })


    function effect(callback) {
        //callbacks.push(callback)
        useReactivities = [];
        callback();
        console.log(useReactivities);

        for (let reactivity of useReactivities) {
            if (!callbacks.has(reactivity[0])) {
                callbacks.set(reactivity[0], new Map())
            }

            if (!callbacks.get(reactivity[0]).has(reactivity[1])) {
                callbacks.get(reactivity[0]).set(reactivity[1], [])
            }

            callbacks.get(reactivity[0]).get(reactivity[1]).push(callback);
        }

    }

    function reactive(object) {
        if (reactivities.has(object))
            return reactivities.get(object);

        let proxy = new Proxy(object, {
            set(obj, prop, val){
                obj[prop] = val;
                
                if (callbacks.get(obj) && callbacks.get(obj).get(prop))
                    for (let callback of callbacks.get(obj).get(prop)) {
                        callback();
                    }
                return obj[prop];
            },
            get(obj, prop) {
                useReactivities.push([obj, prop]);
                if(typeof obj[prop] === "object")
                    return reactivity(obj[prop])
                return obj[prop];
            }
        })

        reactivities.set(object, proxy);

        return proxy;
    }

</script>

效果图:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值