1、knockout建有3个核心特性:
Observables监控属性和dependency tracking依赖跟踪
Declarative bindings 声明式绑定
Templating 模板
2、MVVM and View Models
Model-View-View Model(MVVM) 是为了解决用户交互构建的设计模式,它通常被描述为如何将复杂的UI交互简单的分为三个部分:
A Model: 应用程序的stored data存储数据模型,包含业务领域的对象和操作,并且是独立于UI的,当使用KO时,通常会使用Ajax调用服务端代码去读写这个存储数据模型。
A view model: 纯code表达的UI上的数据和操作,例如你实现一个列表编辑,你的view model通常是一个包含列表项的对象,以及公开出来的添加或移除列表项的方法;注意这个并不是UI本身,它并不包含任何按钮或者显示样式的东西,它也不是持久数据模型,它拥有的是用户使用着的不保存的数据,当使用KO时,你的view model是不包含HTML相关概念的纯javascript对象,保持view model的抽象可以使其保持简单,以便你能够管理更复杂的行为而不至于迷失。
A view: 可见的,交互式的呈现view model状态的UI,它呈现view model的信息,发送命令到view model,并且在view model有任何变化时更新它的状态,当使用KO时,你的view无非就是以声明式绑定方式绑定到view model的HTML文档,或者,你也可以使用模板基于你view model中的数据生成HTML。
使用KO创建view model的方式如下,就像声明任何javascript对象一样
var helloViewModel = { personName: "Bob", age: 123 };
接下来可以使用声明式绑定方式为这个view model创建一个非常简单的view,例如创建一个显示personName的标签
The name is <span data-bind="text: personName"></span>
Activating knockout
data-bind尽管很棒但是它并不是HTML原生属性(它严格遵从HTML5语法,尽管HTML4的验证器会提示有不可识别的属性但是使用它并不会有问题),但是因为浏览器并不认识它,所以需要激活knockout以便使其生效。
激活knockout使用如下的代码块
ko.applyBindings(helloViewModel);
你可以将这个script代码块放在HTML文档的底部,也可以将其放在页面上边并且同时将其包含在ready处理函数中,比如jQuery的$函数中。现在你的view将如同写了如下的HTML代码一样的呈现出来
The name is <span>Bob</span>
ko.applyBindings的参数:
第一个参数是想要用声明式绑定激活的view model,
第二个参数是可选的,用来指定你想要绑定页面的那些部分,例如ko.applyBindings(helloViewModel, document.getElementById('specialElementId')),它约束了可以激活specialElementId的元素及其子元素,这样的好处是可以有多个view model以对应页面的多个区域。
3、Observables
现在你已经知道了怎样创建一个基本的view model并且使用binding显示它的一个属性,然而KO的一个重要的特性是它能够在view model变化时自动的更新画面。KO怎么知道你的view model变化了呢?答案是:你需要将你的model声明成observables的,因为它是一个特殊的javascript对象,能够将变化通知给订阅者,并且能够自动检测依赖。
像下边这样重写之前的view model
var helloViewModel = { personName: ko.observable("Bob"), age: ko.observable(123) }
你完全不需要改变之前的view,同样的data-bind语法仍然工作,不同的是,它现在能够检测到变化,并且当变化发生时自动的更新view.
Reading and writing observables 监控属性的读和写
不是所有的浏览器都支持javascript的getters和setters(比如IE),所以,为了兼容性,ko.observable监控的对象实际上是函数。
读取监控属性(observable)当前的值,只需要调用监控属性,不需要参数。在这个例子中,helloViewModel.personName()将返回“Bob”,helloViewModel.age将返回123.
写入新值到监控属性(observable)上,调用监控属性,并将新的值作为参数传递给它。例如,调用helloViewModel.personName("Mary")将把姓氏更新成Wang.
写入新值到一个model对象的多个监控属性,你可以使用链式语法。例如,helloViewModel.personName("Mary").age("50")将把名字改为Mary,并把年龄改为50
监控属性整个的意义就是它可以被监控,也就是其它的代码可以说它想要得到变化的通知,这就是KO内部会有很多的内置绑定。因此,当你写了data-bind="text:personName", 就会将text的绑定注册到它自身以便当personName变化时得到通知(假设它就像现在这样是可监控的)。
当你使用helloViewModel.personName("Mary")来将名字改为“Mary”时,text的绑定将自动更新对应的DOM元素的文本内容,这就是如何将view model的改变自动的传递到view上。
Explicitly subscribing to observables 监控属性的显示订阅
对于高级用户来说,如果你需要注册自己的订阅到监控属性的更新通知,你可以调用它们的subscribe函数,例如
myViewModel.personName.subscribe(function(newValue)){ alert("The person's new name is " + newValue); }
KO内部工作时使用了大量的subscribe函数调用,大多数时候你不需要用到它,因为内置绑定和模板系统会管理订阅。
subscribe函数接受3个参数: "callback" 是任何时候通知发生时被调用的函数(理解成回调); "tatget"定义callback函数的中this的值,这个参数是可选的;“event”,可选的,默认值为“change”,是接收到的事件名称。
你也可以在你需要的时候终止订阅,首先获取到你的订阅,然后调用的它的dispose函数,例如以下代码:
var subscription = myViewModel.personName.subscribe(function(newValue)){ //do something }); // .... subscription.dispose();
如果你想在监控属性的值变化之前收到通知,你可以订阅beforeChange事件,例如:
myViewModel.personName.subscribe(dunction(oldValue){ alert("The person's previous name is " + oldValue); }, null, "beforeChange");
注意,KO不保证beforeChange和Change事件能够成对触发,因为可以用代码单独触发其中任何一个事件,如果你需要跟踪一个监控属性的旧值,那么你最好使用订阅去检测和跟踪它。
Forcing observables to always notify subscribers 监控属性的强制通知
当向一个监控属性中写入基本类型的值时(如数字,字符,布尔值或null), 监控属性的依赖通常情况只有在值真正改变才会被通知。然后,通过使用内置的通知扩展(notify extender)可以确保监控属性在写入值时它的订阅一定得到通知,即使写入的值和旧值时一样的,你可以如下这样使用该扩展:
myViewModel.personName.extend({ notify: 'always' });
Delaying and/or suppressing change notifications 通知的延迟触发
一般情况下,监控属性一旦变化,它会立刻通知它的订阅,但是如果是一个变化频繁且代价昂贵的监控属性,为了更好的性能你可以限制或者延迟监控属性的变化通知,下边的例子使用了 rateLimit 扩展, 确保personName的变化每50毫秒最多被通知1次:
// Ensure it notifies about changes no more than once per 50-millisencond period myViewModel.personName.extend({rateLimit:50});