proxy模拟实现vue.js的双向绑定

1,183 阅读3分钟

肾么是Proxy

proxy即事件代理,就像中间商,可以代替我们进行一些事件操作。就像我们要去买卖二手物品,但是我们害怕买卖的过程被人欺骗,这时候我们就会找中间商进行操作。而proxy正是这个中间商!

如果你要问我要使用proxy进行操作,那你是不知道proxy究竟有多香!!!

Proxy的快超乎你想象

众所周知,时间复杂度是衡量一个程序好不好极其重要的一个因素。在这个硬件内存不要钱的时代,只要你的代码够快,你就程序就够强!

你要问我Proxy有多快?我给你实战一个大数阶乘看一看吧!

// 阶乘函数
function demo(n) {
    return n == 1?1:n*demo(n-1)
}

//不使用代理模式
console.time('demo');
demo(10000);
console.timeEnd('demo');

let proxy = new Proxy(demo, {
    // 传入函数、环境、参数
    apply(func, obj, argus) {
        console.time('demo2');
        // console.log(argus);
        func(10000)
        console.timeEnd('demo2');
    }
});

//使用代理模式
proxy({},[5]);

image.png

上图中demo是不使用代理模式的定时器,demo2是使用了代理模式的定时器,可以看到速度真的是大大滴提升!

http___img01_sogoucdn_com_app_a_200678_354403bdb4.jpg

如何使用代理模式

在代理模式中会使用到大量的访问器,这里我也就顺带写一下最基本的访问器使用吧!

const user = {
    data: {
        name: '江河',
        age:18
    },
    set age(value) {
        console.log('访问器生效了');
        if (typeof value != 'number' || value < 10 || value > 100) {
            throw new Error('格式错误')
        }
        // 格式没问题设置值
        this.data.age = value;
    },
    get age() {
        // 返回值
        return this.data.age +'岁'
    }
}

user.age = 19;
console.log(user.age); // 19岁

用get/set+属性名来进行定义读写属性就是访问器了。很简单吧,上面我们最后一行获取年龄的时候,按照以往是应该获取18的,但是结果是18岁,这是因为我们使用了访问器,get age()重新定义了获取age属性的方法,在获取的时候自动加上了一个岁。这就是访问器的作用。而且访问器的优先级是大于默认操作的,所以定义后进行属性操作会默认调用访问器。

好的,我们接着聊Proxy吧!

"use strict"
let user = {
    name: "江河",
    age:18
}

// 创建代理对象
// 传入需要代理的对象
// 创建代理方法
const proxy = new Proxy(user, {
    get(obj, key) {
        // console.log(obj[key])
        return obj[key]
    },
    set(obj, key, value) {
        obj[key] = value;
        // 严格模式是需要设置返回为true
        return true;
    }
}); 

// 通过代理获取了属性
console.log(proxy.age) // 18
proxy.name = 'jianghe';
console.log(proxy.name); // jianghe

在上面我们创建了一个代理对象,讲user注入进去,然后使用访问器模式,拿到我们想读写的key,然后使用访问器的方法进行读写操作。这里有一个小细节,在严格模式中set需要返回true,不然系统就会送你一朵小红花,哦不,是小bug。

在这我们可以看到,已经实现了使用了proxy进行数据改变。接下来我们讲Proxy的操作提升亿点点!

proxy模拟实现vue.js的双向数据绑定

才刚看了一个简单的例子就要真刀真枪干了?没错,因为真的不难(内心狂呼“江河,我要下车,这根本不是去幼儿园的车!”,我“车门已经焊死了,想跑没门”)。

在这里我们用两个简单的input框来实现数据双向绑定,改变其中输入框的值,另一个输入框跟随进行改变。

其中思路也是极其简单的,只要我们设定一个对象容器,并将容器中一个属性作为后台数据,两个输入框绑定到一个属性上。然后在输入框上加上监听事件,只要有一个输入框完成输入一个字符就将容器中的值进行更新,然后再输出到两个容器上。没听错,就是这么简单!

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="text" v-model="title">
    <input type="text" v-model="title">
    <div>实现两个输入框的数据双向绑定</div>

    <script>
        function View(){
            // 设置代理
            // 代理设置{}作为容器存储数据
            let proxy = new Proxy({},{
                // 设置对象,参数
                get(obj,item){},
                set(obj, item,value){
                    // console.log(value)
                    // 找到所有的使用模型
                    // 遍历并更新值
                    document.querySelectorAll(`[v-model="${item}"]`).forEach(e=>{
                        e.value = value
                    })
                }
            })
            this.init = function(){
                // 设置绑定事件
                // 找到所有的触发事件文本框
                const model = document.querySelectorAll("[v-model]");
                // 给所有文本框添加监听机制
                model.forEach(item=>{
                    // 监听键盘弹起事件,即完成一个字符的输入
                    item.addEventListener('keyup',function(){
                        // console.log(1)
                        // 给容器的值设置为最新的值
                        proxy[this.getAttribute('v-model')] = this.value;
                    })
                })

            }
        }
        // new 出实例并运行方法
        new View().init();
    </script>
</body>
</html>

成果如下:

image.png

我是江河,前端实习生一枚,正在准备春招,欢迎各位大佬滴滴,文章如有不正之处敬请斧正!