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>
时间: 2024-10-09 20:50:37

Knockout双向绑定的相关文章

Knockout 双向绑定的理解

今天做了个需求就是上传图片,然后在代码中通过jQuery给一个标签赋值,经过前台的debug,发现这个值赋值成功了,但是提交到后台的请求里就没了,然后经历了一顿度娘,结果中发现了问题. 既然knockout是双向绑定的,那么什么时候会做这个双向绑定的动作呢?结果就是会在文本框触发了change事件才会做双向绑定,所以说,手动通过jQuery来修改的时候,需要自己再手动触发次change $("#XXX").val("XXX").change()

Knockout基本绑定数据

官方地址:www.knockoutjs.com //使用require.js加载 requirejs(['knockout', 'knockout.mapping.js'], function (ko, komap) { //这个是自己封装的从服务器获取数据的方法 var _model = GetModelFromServer(); //一个插件,将model转为可以双向绑定数据 _viewListModel = komap.fromJS(data); //开始绑定,第一个参数:modelVie

ASP.NET Web API实践系列07,获取数据, 使用Ninject实现依赖倒置,使用Konockout实现页面元素和视图模型的双向绑定

本篇接着上一篇"ASP.NET Web API实践系列06, 在ASP.NET MVC 4 基础上增加使用ASP.NET WEB API",尝试获取数据. 在Models文件夹下创建Comment类: namespace MvcApplication5.Models { public class Comment { public int ID { get; set; } public string Author { get; set; } public string Text { ge

Angular数据双向绑定

Angular数据双向绑定 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前端JS框架,已经被用于Google的多款产品当中.AngularJS有着诸多特性,最为核心的是:MVVM.模块化.自动化双向数据绑定.语义化标签.依赖注入等等. 一.什么是数据双向绑定 Angular实现了双向绑定机制.所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面. 一个最简单的示例就是这样: <div ng-control

【vue】跟着老马学习vue-数据双向绑定

学习了node.js教程,只能说是有了一定的了解,之前也了解了webpack和es6的核心内容,也看过vue2.0的官网教程,并结合视频看过项目,但是理解和运用仍然存在很多问题,接下来的一段时间,跟着老马学习vue 学习链接:http://aicoder.com/vue/preview/all.html#1 vue最大的特点就在于它的双向绑定,是一个前端的双向绑定类的框架. 一说到vue我们就应该立刻想到以下几部分:1.数据双向绑定:2.列表渲染.条件渲染:3.事件处理:4.生命周期:5.组件化

原生js实现数据双向绑定

最近接触了vue,在谈到vue等等的mvvm框架之前,先了解什么是数据双向绑定以及如何利用原生JS实现数据双向绑定 单向数据绑定 指先把模板写好,然后把模板和数据(数据可能来自后台)整合到一起形成HTML代码,然后把这段HTML代码插入到文档流里 缺点:一旦HTML代码生成就没有办法改变,如果有新数据重新传入,就必须重新把模板和数据整合到一起插入到文档流中 数据双向绑定 数据模型和视图之间的双向绑定,用户在视图上的修改会自动同步到数据模型中,同样的,如果数据模型中的值发生变化,也会同步到视图中去

AngularJS学习笔记(三)数据双向绑定

双向绑定 双向绑定是AngularJS最实用的功能,它节省了大量的代码,使我们专注于数据和视图,不用浪费大量的代码在Dom监听.数据同步上,关于双向更新,可看下图: 下面,我们通过代码来实现.先不要纠结其中不明白的地方,先来体验下数据绑定的效果. 数据-->视图 这里我们只演示有了数据以后,如何绑定到视图上. <!DOCTYPE html> <html ng-app="App"> <head> <script type="tex

Angular指令封装jQuery日期时间插件datetimepicker实现双向绑定

一放假就高产似母猪了. 00.混乱的前端界 Angular1.x确实是个学习成本很高的框架,刚开始实习那会儿,前端啥也不懂,工头说用Angular,我们这群小弟也只能硬着头皮学.在这之前,前端的东西大部分都用的jQuery,而Angular正好是和jQuery的思维是相反的,开发过程中遇到了不少坑.而Angular团队也放弃了1.x开始开发和React神似的2.0版本,唉,真是沧海桑田啊. 01.Angular vs jQuery Angular模块化和解耦的思路确实值得一学,但是相对于成熟的j

双向绑定 使得函数通过依赖管理系统仅仅需要声明需要的协作对象,而不需要知道从哪里来

双向绑定,即MVVM,也就是数据影响视图,视图也会影响数据. 一个M影响V最简单的示例就是这样: <div ng-controller="CounterCtrl"> <span ng-bind="counter"></span> <button ng-click="counter++">increase</button> </div> function CounterCtrl