zoukankan      html  css  js  c++  java
  • mvvm双向绑定机制的原理和代码实现

    mvvm框架的双向绑定,即当对象改变时,自动改变相关的dom元素的值,反之,当dom元素改变时,能自动更新对象的值,当然dom元素一般是指可输出的input元素。

    1. 首先实现单向绑定,在指定对象的属性值发生改变时触发callback函数。
    2. 单向绑定可采用ES5新增的defineProperty实现(或defineProperties),用了ES5注定就不支持IE9以下了,为了防止递归死循环问题,原有属性需要剪切到一个私有属性中保存。
    3. 循环调用defineProperty定义闭包时产生作用域的问题,为解决作用域变量对象的值会取到最后一次运行值问题,多定义一层立即调用的闭包函数将值传入。
    4. 我们定义getFN和setFN函数用于在属性get和set的时候触发,它的功能是对私有属性__private的读写并触发回调函数通知UI层更新界面。
    5.单向绑定实现完成后,实现反向的绑定,即UI层onchange之后触发更新数据,这个相对比较容易,在dom中通过自定义属性bindKey关联model的值变化,监听使用oninput事件,相比onchange的好处是可以实时变化不用等失焦,而且对右键粘贴、菜单粘贴,拖动文字进文本框等方式都可以触发,完全无死角,缺点是只支持IE9以上,但是在IE9以下有等价的onpropertychange可以用还是能兼容的。
    6.总结,双向绑定的原理并不复杂,整体代码不超过50行,非常精简,不过还是有一些技术含量,下面是完整的代码,如果不想使用庞大的框架,可以用一下。ie9以下是不支持的,如要支持ie9以下可以使用avalon,它用vbs做了get,set存取器的封装,这点还是比较强大的。

    html:

    <div id="container">
       <p>
       name:<input type="text" bindkey="userName">
       </p>
       <p>
       age:<input type="text" bindkey="age">
       </p>
    <div>

    js:

     <script type="text/javascript">
        window.Model={
            userName:"windy",
            age:34,
            skill:["javascript","html","css","jquery","node"],
            
        }
        function bindingModel(model,changeCallback){
            var propertiesMap={};
            model.__private={};
            function getFn(name){
                var result=this.__private[name]
                console.log("get value:"+name+"="+ result);
                return result;
            };
            function setFn(name,val){
                if(this.__private[name]!=val){
                    console.log("set value:"+name+"="+val);
                 
                    this.__private[name]=val;
    
                    if(changeCallback){
                        changeCallback(name,val);
                    }
                }
            };
            for(elem in model){
                if(model.hasOwnProperty(elem) && elem!="__private" && typeof(model[elem])!="function"){
                    (function(propName,propValue){
                        model.__private[propName]=propValue;// init value
                        propertiesMap[propName]={
                            get:function(){ return getFn.call(this,propName)},
                            set:function(v){ return setFn.call(this,propName,v)},
                            //value:model[elem],
                            //writable: true,
                            enumerable: true,
                            configurable: true
                        }
                    })(elem,model[elem]);
                }
            }
            Object.defineProperties(model,propertiesMap)
            
        }
        function bindingBoth(model,dom){
            dom.find("[bindkey]").each(function(item){
                var key=$(this).attr("bindkey");
                $(this).val(model[key]);
                $(this).bind("input",function(){
                   model[key]=$(this).val();
                })
            });
            bindingModel(model,function(name,val){
                var el=dom.find("[bindkey="+name+"]");
                if(el.val()!=val){
                    el.val(val);
                }
                
            });
        }
        bindingBoth(window.Model,$("#container"))
        </script>
  • 相关阅读:
    QtDBus编程详解
    QProcess详解
    python 爬虫 亚航 指定日期间的航线
    python 模块
    centos postgres 安装、远程连接
    python 爬虫 anyproxy
    python_scrapy_filespipe重写
    python_xpath
    常见问题汇总
    python_scrapy_log日志
  • 原文地址:https://www.cnblogs.com/windyfancy/p/5176963.html
Copyright © 2011-2022 走看看