美文网首页
从 0.5 开始造轮子 仿 vue 的 mvvm(二)

从 0.5 开始造轮子 仿 vue 的 mvvm(二)

作者: WEB_Jorie | 来源:发表于2018-07-06 14:03 被阅读0次

    替换数据劫持对象

      上一篇实现了 mvvm 实现思路,可是不够优雅还有很多问题,我先解决这个问题数据劫持的问题。

    之前的数据劫持

    之前数据的劫持试是这么做的

    // 重写data 的 get set  更改数据的时候,触发watch 更新视图
    myVue.prototype._observer = function (obj) {
        var _this = this;
        for (key in obj){  // 遍历数据
            //订阅池
            // _this._watcherTpl.a = [];
            // _this._watcherTpl.b = [];
            _this._watcherTpl[key] = {
                _directives: []
            };
            let value = obj[key]; // 获取属`性值
            let watcherTpl = _this._watcherTpl[key]; // 数据的订阅池
            Object.defineProperty(_this._data, key, { // 数据劫持
                configurable: true,  // 可以删除
                enumerable: true, // 可以遍历
                get() {
                    console.log(`${key}获取值:${value}`);
                    return value; // 获取值的时候 直接返回
                },
                set(newVal) { // 改变值的时候 触发set
                    console.log(`${key}更新:${newVal}`);
                    if (value !== newVal) {
                        value = newVal;
                        //_this._watcherTpl.xxx.forEach(item)
                        //[{update:function(){}}]
                        watcherTpl._directives.forEach((item) => { // 遍历订阅池
                            item.update();
                            // 遍历所有订阅的地方(v-model+v-bind+{{}}) 触发this._compile()中发布的订阅Watcher 更新视图
                        });
                    }
                }
            })
        };
    };
    

    这么做是可以实现可是,可以看到有这么一些缺点:

    • 对象必须是存在的。
    • 循环耗费性能。
    • 代码可读性可拓展性不是很好
    • 等等..
      那么我们能不能换一种方式去解决数据的劫持问题?

    Proxy 横空出世

    Proxy 是 ECMAScript 2015 的新特性,唯一的 缺点是 兼容性不是非常好。但我们要团结啊,哈哈哈。 废弃 IE。。。
    下面我们将使用 Proxy 实现数据的劫持 和 代理。关于 Proxy 可以看这么两篇文章,一个是 阮一峰老师 的,不管阮一峰怎么样,当初竟然帮助过我们,我觉得就可以称之为老师 ,还有一篇 抱歉,学会 Proxy 真的可以为所欲为

    // 重写data 的 get set  更改数据的时候,触发watch 更新视图
    myVue.prototype._observer = function (obj) {
        const _this = this;
        this._data = new Proxy(obj, { // 数据劫持
            get(target, key, receiver) {
                return Reflect.get(target, key, receiver); // 获取值的时候 直接返回
            },
            set(target, key, newVal) { // 改变值的时候 触发set
                if (_this.value !== newVal) {
                    _this.value = newVal;
                    //先将数据更新完成后
                    let res =  Reflect.set(target,key,newVal);
                    _this._watcherTpl[key]._directives.forEach((item) => { // 遍历订阅池
                        item.update();
                    });
                    return res
                }
            }
        });
    };
    

    看到代码不用说了,量级的差距,简洁多了,这里直接将 VUE 的data 变成了一个 Proxy 对象。进行数据的操作。
    既然这里更改了,那么我们之前的订阅池其实是废除了,因为没有循环了不存在 key:

       _this._watcherTpl[key] = {
                _directives: []
            };
    

    所以我这里单独在_compile 处理了订阅池。

     const attrVal = node.getAttribute('v-model'); // 获取绑定的data
                    _this.hasDirectives(attrVal);
    
    //工具类判断是否有订阅池
    myVue.prototype.hasDirectives = function (attr) {
        const _this = this;
        // 没有事件池 创建事件池
        if (!_this._watcherTpl[attr]) {
            _this._watcherTpl[attr] =  {};
            _this._watcherTpl[attr]._directives = [];
        } else {
            if (!_this._watcherTpl[attr]._directives) {
                _this._watcherTpl[attr]._directives = []
            }
        }
    };
    

    这样就解决了连接池的问题 ,这里的连接池使用的是数组,后面我们将会替换为map

    结语

    github完整实现

    在线地址,需要翻墙

    相关文章

      网友评论

          本文标题:从 0.5 开始造轮子 仿 vue 的 mvvm(二)

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