Knockout雙向綁定

knockout雙工綁定基於 observe 模式,性能高。核心就是observable對象的定義。這個函數最後返回了一個也叫作 observable 的函數,也就是用戶定義值的讀寫器(accessor)。javascript

this.firstName=ko.observable(「Bert」);
this.firstName();
this.firstName(「test」);

ko.observable作了什麼java

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(); 執行的。node

ko.computed作了什麼express

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使用閉包

一、ko綁定方式,當即執行用於須要後處理的一些數值app

//點擊事件
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後面ide

二、event事件函數

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

完整的 key press 過程分爲兩個部分,按鍵被按下,而後按鍵被鬆開並復位。
當按鈕被鬆開時,發生 keyup 事件。它發生在當前得到焦點的元素上。
keydown事件發生在鍵盤的鍵被按下的時候,接下來觸發 keypress事件。  keyup 事件在按鍵被釋放的時候觸發。
KeyPress 只能捕獲單個字符;KeyDown 和KeyUp 能夠捕獲組合鍵。 

三、性能

self.weeklyRecommend(this);  //監控對象總體發生變化時響應
self.weeklyRecommend(ko.mapping.fromJs(this));  //能夠監控對象下每一個元素的改變

四、ko事件註冊ui

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」>

五、事件冒泡

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

六、$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>
相關文章
相關標籤/搜索