Proxy实现双向绑定

Proxy 对象用于定义基本操作的自定义行为(如属性查找、赋值、枚举、函数调用等)。 对于Proxy对象的任何操作都可以被捕获到,这也是实现双向绑定的基础,而Object.defineProperty()缺没法捕获到对象新增的属性,vue3也使用proxy替代了 defineProperty

 <!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>proxy</title>
</head>

<body>
    <input type="range" min='0' max='255' id='r'>
    <input type="range" min='0' max='255' id='g'>
    <input type="range" min='0' max='255' id='b'>

    <div id='color' style="width: 100px;height: 100px;">

    </div>
</body>

</html>
<script>
    let handlers = new Map();
    let reactivities = new Map();
    let usedReactivities = []
    function reactive(obj) {
        //防止多个相同obj的reactive对象
        if (reactivities.has(obj)) {
            return reactivities.get(obj)
        }
        let proxy = new Proxy(obj, {
            get(obj, prop) {
                console.log(obj, prop);
                //收集依赖
                usedReactivities.push([obj, prop])
                //解决a.b.c的情况
                if (typeof obj[prop] === 'object') {
                    return reactive(obj[prop])
                }
                //一定要return,不然obj[prop]拿不到值
                return obj[prop]
            },
            set(obj, prop, val) {
                obj[prop] = val;
                // console.log(handlers, 0);
                //只执行与prop有关的handler 
                if (handlers.get(obj)) {
                    // console.log(handlers.get(obj), 1);

                    if (handlers.get(obj).get(prop)) {
                        // console.log(handlers.get(obj).get(prop), 2);

                        for (const handler of handlers.get(obj).get(prop)) {
                            handler()
                        }
                    }
                }
                return obj[prop]
            }
        });
        reactivities.set(obj, proxy)
        return proxy
    };

    function effect(handler) {
        usedReactivities = [];
        //调用时访问属性,proxy就会执行get方法..任何操作proxy对象的行为都会被收集到
        handler();
        for (const usedReactivity of usedReactivities) {
            let [obj, prop] = usedReactivity;
            console.log(obj, prop);
            if (!handlers.has(obj)) {
                handlers.set(obj, new Map())
            }
            if (!handlers.get(obj).has(prop)) {
                handlers.get(obj).set(prop, [])
            }
            //存放prop的有关操作的执行函数
            handlers.get(obj).get(prop).push(handler)
            // console.log(handlers.get(obj).get(prop),'prop');

        }
    }
    let p = reactive({ r: 100, g: 255, b: 100 })

    effect(() => {
        document.getElementById('r').value = p.r
    });
    effect(() => {
        document.getElementById('g').value = p.g
    });
    effect(() => {
        document.getElementById('b').value = p.b
    });
    document.getElementById('r').addEventListener('input',(event)=>{
        p.r=event.target.value
    });
    document.getElementById('g').addEventListener('input',(event)=>{
        p.g=event.target.value
    });
    document.getElementById('b').addEventListener('input',(event)=>{
        p.b=event.target.value
    });
    effect(()=>{
        document.getElementById('color').style.backgroundColor=`rgb({p.r},{p.g},${p.b})`
    });

</script>