美文网首页Web前端之路Web前端之路程序员
打造属于自己的MVVM框架: 3.双向绑定

打造属于自己的MVVM框架: 3.双向绑定

作者: Pursue | 来源:发表于2016-05-30 21:22 被阅读326次

    MVVM中对Bingding的解析只能算viewModel->view的单项绑定,但MVVM绝不仅仅只有单向绑定,更重要的是如何监控viewModel变化,将信息实时的反馈给view。

    原文请戳
    源码请戳

    如何监控Object的变化

    你会可能会遇到一下场景:前端UI已经渲染完成,但并没有数据,因此发送请求向服务器请求数据,AJAX回调完成后,用Callback里的值去更新UI(很可能是暴力的Jquery);当前端根据渲染的数据再次做了操作后,又得去DOM中取出修改后的值,一来一去十分麻烦,而且一旦UI结构有变化,那简直会被玩死。

    而MVVM的最大好处就是避免这种复杂的DOM操作,取而代之的是监控绑定在页面上的observable对象,然后实时的更新DOM,整个过程均为自动化。

    那么问题来了,如何监控一个对象的变化呢?

    方案1.Object.observe

    Object.observe将会成为ECMAScript的标准,它不需要任何的库就能实现对象的变化的监控。如下:

    // Let's say we have a model with data
    var model = {};
    // Which we then observe
    Object.observe(model, function(changes){
    
        // This asynchronous callback runs
        changes.forEach(function(change) {
    
            // Letting us know what changed
            console.log(change.type, change.name, change.oldValue);
        });
    
    });
    

    在Chrome调试如下:

    model.name = "xiaofei";

    add name undefined

    "xiaofei"

    可以看到,Object.observe十分强大,它不但能监控对象的变化,而且连变化的类型都知道,十分的方便。
    然而,狗血的事情来了。正如我上面所说,它只是未来ECMA的标准,因此属于非标准的用法。兼容性如下:

    方案2.Object.defineProperty

    Object.defineProperty可以在对象上新添一个自定义的属性,属性的所有设置都有自己配置(是否可枚举,是否可读写等),十分灵活。这些并不能实现对象的监控,但它提供了getset的属性器以及事件回调,因此我们可以利用这个set事件去实现对象的监控。

    代码如下:

    
    ko.observable = function(defaultValue) {
        var self = {};
        var value = defaultValue;
        var fn = function(val) {
            if(val) {
                self.value = val;
            } else {
                return self.value;
            }
        };
        fn.isObservable = true; 
        Object.defineProperty(self, 'value', {
            get: function() {
                return value;
            },
            set: function(val) {
                value = val;
                console.log("here I can capture changes event");
            }
        });
    
        return fn;
    }
    
    

    思路如下:

    ko.observable方法会将对象封装为一个observable对象,该对象的值由Object.defineProperty定义,因此当observable更新值时会触发Object.defineProperty的set方法,此时可以认为该对象变化了。

    在Chrome下测试结果为:

    var o = observable("xiaofei");

    undefined

    o();

    "xiaofei"

    o("new name")

    here I can capture changes event

    undefined

    o()

    "new name"

    看起来似乎不错,可是为了实现foreach,我们还需要另外创建一个observableArray对象,毕竟数组和基本的对象不太一样,参考上面的实现:

    ko.observableArray: function(defaultValue) {
        if(defaultValue && defaultValue.constructor.name != 'Array') {
            throw "observableArray param must be array."
        }
        var self = {};
        var value = defaultValue;
        var fn = function(val) {
            if(val) {
                self.value = val;
            } else {
                return self.value;
            }
        };
        fn.isObservable = true; 
        var keys = ["pop", "push", "reverse", "shift", "sort", "splice", "unshift", "concat", "join", "slice", "indexOf", "lastIndexOf", "forEach", "every", "map", "some", "reduce", "reduceRight", "each", "clone", "min", "max", "average", "sum", "unique", "shuffle", "pluck"];
        for(var i in keys) {
            var key = keys[i];
            fn[key] = (function(key) {
                return function() {
                    var elements = ko.unwrap(fn);
                    elements[key] && elements[key].apply(elements, arguments);
                    fn(elements);
                };
            })(key);
        }
        Object.defineProperty(self, 'value', {
            get: function() {
                return value;
            },
            set: function(val) {
                value = val;
                console.log("here I can capture changes event");
    
            }
        });
        return fn;
    }
    
    

    observableArray的实现在observable的基础上,继承了Array的操作,因为对于observableArray对象来说,更新元素里面的值的属性并不算它更新了,而只有它的个数活着自身的集合发生变化时才认为是变化了。而在这里我没有直接遍历所有Array的方法是因为Array的方法默认是不可迭代的,所以只能一一列出,甚至,根本不需要这么多方法,完全可以根据实际情况去定制。

    在Chrome测试如下:

    var arr = ko.observableArray([1,2,3]);

    undefined

    arr.push(4)

    here I can capture changes event

    undefined

    arr()

    [1, 2, 3, 4]

    arr.pop();

    undefined

    arr()

    [1, 2, 3]

    5.总结

    这一节主要介绍了如何实现双向绑定中的对象监控,实现了这个功能后,可以在事件捕获时再次通知模版引擎去重新渲染UI,这样就形成了双向通信,下一篇将会进行整合。

    原文请戳
    源码请戳

    相关文章

      网友评论

      • 2796da4c3a39:脑补了一下😀😀
        Pursue:@2796da4c3a39 嗯,有收获我就没白写
        2796da4c3a39: @Pursue 写的很好啊,看这个文章明白了很多
        Pursue:@2796da4c3a39 什么意思?

      本文标题:打造属于自己的MVVM框架: 3.双向绑定

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