Knockout双向绑定

knockout双工绑定基于 observe 模式,性能高。核心就是observable对象的定义。这个函数最后返回了一个也叫做 observable 的函数,也就是用户定义值的读写器(accessor)。

this.firstName=ko.observable(“Bert”);
this.firstName();
this.firstName(“test”);

ko.observable做了什么

ko.observable = function (initialValue) {
    var _latestValue = initialValue; //保留上一次的参数,与observable形成闭包
     
    function observable() {
        if (arguments.length > 0) {
            // Write,Ignore writes if the value hasn't changed
            if (observable.isDifferent(_latestValue, arguments[0])) {
                observable.valueWillMutate();
                _latestValue = arguments[0];
                if (DEBUG) observable._latestValue = _latestValue;
                observable.valueHasMutated();
            }
     
            return this; // Permits chained assignments
        }
        else {
            // Read
            ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
            return _latestValue;
        }
    }
    ko.subscribable.call(observable);
    ko.utils.setPrototypeOfOrExtend(observable, ko.observable['fn']);
     
    if (DEBUG) observable._latestValue = _latestValue;
    /**这里省略了专为 closure compiler 写的语句**/

    return observable;
     
}

通过 ko.subscribable.call(observable); 使这个函数有了被订阅的功能,让 firstName 在改变时能通知所有订阅了它的对象。其实就是维护了一个回调函数的队列,当自己的值改变时,就执行这些回调函数。根据上面的代码,回调函数是在 observable.valueHasMutated(); 执行的。

ko.computed做了什么

this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();    
    }, this);
$.computed = function(obj, scope){
    //computed是由多个$.observable组成
    var getter, setter
    if(typeof obj == "function"){
        getter = obj
    }else if(obj && typeof obj == "object"){
        getter = obj.getter;
        setter = obj.setter;
        scope  = obj.scope;
    }
    var v
    var ret = function(neo){
        if(arguments.length ){
            if(typeof setter == "function"){//setter不一定存在的
                if(v !== neo ){
                    setter.call(scope, neo);
                    v = neo;
                }
            }
            return ret;
        }else{
            v = getter.call(scope);
            return v;
        }
    }
    return ret;
}
$.dependencyDetection = (function () {
    var _frames = [];
    return {
        begin: function (ret) {
            _frames.push(ret);
        },
        end: function () {
            _frames.pop();
        },
        collect: function (self) {
            if (_frames.length > 0) {
                self.list = self.list || [];
                var fn = _frames[_frames.length - 1];
                if ( self.list.indexOf( fn ) >= 0)
                    return;
                self.list.push(fn);
            }
        }
    };
})();
$.valueWillMutate = function(observable){
    var list = observable.list
    if($.type(list,"Array")){
        for(var i = 0, el; el = list[i++];){
            el();
        }
    }
}

双向绑定如何实现

$.buildEvalWithinScopeFunction =  function (expression, scopeLevels) {
    var functionBody = "return (" + expression + ")";
    for (var i = 0; i < scopeLevels; i++) {
        functionBody = "with(sc[" + i + "]) { " + functionBody + " } ";
    }
    return new Function("sc", functionBody);
}
$.applyBindings = function(model, node){       
   
    var nodeBind = $.computed(function (){
        var str = "{" + node.getAttribute("data-bind")+"}"
        var fn = $.buildEvalWithinScopeFunction(str,2);
        var bindings = fn([node,model]);
        for(var key in bindings){
            if(bindings.hasOwnProperty(key)){
                var fn = $.bindingHandlers["text"]["update"];
                var observable = bindings[key]
                $.dependencyDetection.collect(observable);//绑定viewModel与UI
                fn(node, observable)
            }
        }
    },node);
    return nodeBind
     
}
$.bindingHandlers = {}
$.bindingHandlers["text"] = {
    'update': function (node, observable) {
        var val = observable()
        if("textContent" in node){
            node.textContent = val;
        }
    }
}
window.onload = function(){
    var model = new MyViewModel();
    var node = document.getElementById("node");
    $.applyBindings(model, node);
}

KO使用

1、ko绑定方式,立即执行用于需要后处理的一些数值

//点击事件
data-bind="click:$root.fun1.bind($param1,param2)"
//立即执行
data-bind="attr: { src : $root.fun2(param1,param2) }”
//缺省参数
data-bind="event: { mouseover: myFunction }"
<script type="text/javascript">  
    var viewModel = {  
        myFunction: function(data, event) {  
            if (event.shiftKey) {  
                //do something different when user has shift key down  
            } else {  
                //do normal action  
            }  
        }  
    };  
    ko.applyBindings(viewModel);  
</script>

注意:在bind方式传递参数时,data和event两个参数依然被缺省传递。 新加入的参数,在使用时排在第一位,定义时只能排在$data后面

2、event事件

<input type="text" placeholder="输入关键字搜索" data-bind="event:{keyup:$root.fun1.bind($data,$element)}">

完整的 key press 过程分为两个部分,按键被按下,然后按键被松开并复位。
当按钮被松开时,发生 keyup 事件。它发生在当前获得焦点的元素上。
keydown事件发生在键盘的键被按下的时候,接下来触发 keypress事件。  keyup 事件在按键被释放的时候触发。
KeyPress 只能捕获单个字符;KeyDown 和KeyUp 可以捕获组合键。 

3、

self.weeklyRecommend(this);  //监控对象整体发生变化时响应
self.weeklyRecommend(ko.mapping.fromJs(this));  //可以监控对象下每个元素的改变

4、ko事件注册

ko.bindingHandlers.singleExamHover = {
    init: function(element, valueAccessor){
        $(element).hover(
            function(){
                //todo 
            },
            function(){
                //todo
            }
        );
    },
    update:function(element, valueAccessor){
        var _value = ko.unwrap(valueAccessor());
        if(_value){
            $(element).addClass("current");
        }else{
            $(element).removeClass("current");
        }
    }
};
<div class="h-set-homework" data-bind="singleExamHover:question.checked”>

5、事件冒泡

By default, Knockout will allow the click event to continue to bubble up to any higher level event handlers。

If necessary, you can prevent the event from bubbling by including an additional binding that is named clickBubble and passing false to it 

<div data-bind="click: myDivHandler">
    <button data-bind="click: myButtonHandler, clickBubble: false">
        Click me
    </button>
</div>

Normally, in this case myButtonHandler would be called first, then the click event would bubble up to myDivHandler. However, the clickBubble binding that we added with a value of false prevents the event from making it past myButtonHandler

6、$data

This is the view model object in the current context. In the root context,  $data and  $root are equivalent. Inside a nested binding context, this parameter will be set to the current data item (e.g., inside a  with: person binding,  $data will be set to  person).  $data is useful when you want to reference the viewmodel itself, rather than a property on the viewmodel.
<div data-bind="click:changeEditor.bind($data,$element,param1,param2)"></div>
<script>
changeEditor : function(ele,param1,param2){
     console.log(this)
     console.log(ele==event.currenttarget)
}
</script>

 

转载于:https://www.cnblogs.com/chenlogin/p/6629361.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值