再聊一下用Proxy实现双向数据绑定(含数组)

上一篇的末尾,笔者简单介绍了Object.defineProperty在数组监控方面的不足以及其替代品Proxy。可是对于前者总感受还少点什么,emmmmm...好像是demo,因而笔者精心准备了一下。因此本篇会主要会分红两大块:一是讲述如何弥补Object.defineProperty先天不足的状况下实现对数组的精准监控,二是着重用Proxy实现双向数据绑定。node

Object.defineProperty

首先呢,对于 Object.defineProperty在数组监控方面的不足,咱们不只要知其然,更要知其因此然。所以先用栗子来证明下这个 不足。上代码:
let data = {
    list: []
}

Object.keys(data).forEach(function (key) {
    let value = data[key];
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
            return value;
        },
        set(newValue) {
            console.log(`Setting`);
            value = newValue;
            return true;
        }
    })
})

data.list.push(1);                 //--->  A
// data.list = [1, 2, 3];                //--->  B
console.log(data.list);
事先咱们须要明确下:因此对数组的监控,就是能监测到数组元素的 增长或者删除。所以按照‘理论’,上述代码若是向 list 中增长元素的时候,理应会有打印 Setting(笔者已经标记了最后的 A行 和 B行)

在只注释 B行状况下看运行结果:git

/usr/local/bin/node --inspect-brk=17809 demo.js 
Debugger listening on ws://127.0.0.1:17809/48911764-8533-4c95-a501-20384c924f6a
Debugger attached.
Array(1) [1]

能够看出,虽然咱们成功得向数组里增长了一个元素,可是并无打印出 Setting,所以就是说并无监测到数组的变化.es6

在只注释 A行状况下看运行结果:segmentfault

/usr/local/bin/node --inspect-brk=27303 demo.js 
Debugger listening on ws://127.0.0.1:27303/2fac4f06-e775-4485-b70b-b2660a98c2b8
Debugger attached.
Setting
Array(3) [1, 2, 3]

咱们成功得向数组里增长了一个元素也没有打印出 Setting,说明数组的变化被监测到了.源码数组

因此咱们能够大胆得猜想:当监控 数组 数据对象的时候,实质上就是监控数组的 地址,地址不变也就不会被监测到,因此咱们向 listpush 元素的时候并无触发打印;当咱们直接替换 list 对象的时候就触发了打印。因此这就是 Object.defineProperty在数组监控方面的不足。

下面就用例子介绍下如何弥补这方面的不足(固然,也是Vue的处理方式)app

其核心思想就是 覆写 数组对象中的方法,在调用数组方法的同时能触发回调。下面是核心代码:
let arrayMethod = Object.create(Array.prototype);
    ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
        Object.defineProperty(arrayMethod, method, {
            enumerable: true,
            configurable: true,
            value: function () {
                let args = [...arguments]
                Array.prototype[method].apply(this, args);
                console.log(`operation: ${method}`)
                dep.notify();
            }
        })
    });
    
[Array Object].__proto__ = arrayMethod;
同时这样作的好处是,仅仅是数据中的数组对象的原型被修改掉了,并不会影响到全局的数组对象。

而后笔者也作了一个例子,以下:框架

clipboard.png

经过向 list 中添加随机数或者删除随机数,能同步渲染到页面上,源码在这函数

Proxy

Proxy 意为 代理,通俗来讲就是在 目标数据对象 外围设置一层 拦截
举个形象的栗子:有一个 仓库,一开始你们能够任意得存放货物,后来老板请了一个 仓库管家,全部人想要存放货物的必需要通过管家的手。并且老板还给管家制定了一套 管理标准,管家对仓库的管理必须严格按照这套标准。

根据这个栗子,罗列了三个关键词:仓库仓库管家管理标准,下面用一段简单的代码来表述下, 若是想全面得学习Proxy, 请参考阮一峰老师的博客学习

let dataProxy = new Proxy(data, {
    set() {

    },
    get() {

    }
})

那么代码与栗子的对于关系就出来了:this

仓库 --- data
仓库管家 --- Proxy / dataProxy
管理标准 --- {set(),get()}

并且能够注意到 Proxy 实际上是个构造函数,全部咱们对原数据对象的操做都得经过构造函数new出来的对象 dataProxy就好像全部货物的存取都得经过管家同样

而后接下来笔者用 Proxy 写了一个栗子,用来 重现 上面对数组的监控,核心代码以下:

//数据源
let vm = {
    list: [1, 2, 3, 4]
}

let vmProxy = new Proxy(vm.list, {
        set(target, prop, value) {
            console.log(`Setting: ${value}`);
            Reflect.set(target, prop, value);
            dep.notify();
            return true;
        }
    })

咱们能够注意到咱们并无直接将 vm 用Proxy进行“包装”,而是将vm.list进行“包装”。由于笔者在学习的时候发现若是直接用 vm 的话,当咱们向 list 中添加元素的时候并不会被监测到,笔者猜想的缘由和 Object.defineProperty 同样,栗子呈上。若是有知道缘由的小伙伴,请多多指教。
篇幅有限,这个例子的代码直接上连接了,有兴趣的小伙伴能够直接down下来看。

数组说完了,剩下就普通类型的数据就是按照正常套路走就对了。另外,笔者此次吸收上篇博客的教训,将代码写得更详尽,实现方式更贴近框架级别。用Proxy实现数据的双向绑定,栗子的功能和上篇博客是同样的,只是内容更加饱满。该栗子中笔者简单实现了一个简单版的类Vue的框架 View,包含Html文档的解析、Watcher的构造、数据的绑定等,代码简单不复杂,因此这里就不赘述了,我以为千言万语不敌一段代码,那么相关的代码就此呈上

感谢小伙伴的捧场!我会坚持写博客,分享本身的见闻和工做中遇到的坑,共同窗习,共同进步

相关文章
相关标签/搜索