Knockout.js入门
Knockout.js 是一个JavaScript库,它可以让你声明绑定元素和其对应的数据模型,达到你的UI和模型自动双向更新。Knockout入门不难,并且它很好地集成了其他的类库和技术。通过本篇文章你可以入门。
为什么是Knockout
开发一个复杂的、动态的以数据驱动的(data-driven数据驱动)网页应用是一个具有挑战的任务。当用户进行操作、或新的数据被加载时,保持用户界面与底层数据正常同步,涉及到关联大量的事件来控制各种元素与数据的中间数据信息。
Knockout之前
用一个简单的方法来证明Knockout的优势,重要的是了解如何使用Knockout创建简单的编辑器,和不用Knockout是如何创建的。 假设编辑器有一个层元素用来显示条目名称,以及一个输入框用来编辑名称:
<div id="itemName"></div>
<input type="text" id="itemNameEdit" />
使用jQuery,你可以关联一组数据和他们对应数据的交互,如下:
var item = {
id: 88,
name: "Apple Pie"
};
$("#itemName").text(item.name);
$("#itemNameEdit").val(item.name).change(function() {
item.name = $(this).val();
$("#itemName").text(item.name);
});
这些代码没有什么难度,但是对于一个应用,需要管理的UI和数据的交互会越来越多,这类代码会持续增加,直到很难维持。此外,这种类型的代码是通过设置特定标识紧密耦合的,很难重用。
添加一些数据绑定
Knockout可以让你通过你的标识使用声明将你的UI和数据轻松关联绑定。在一般情况下,你的元素不再需要ID和类,除非用于样式。Knockout的绑定中,指定data-bind
属性到一个单独的元素上:
<div data-bind="text: name"></div>
<input type="text" data-bind="value: name" />
现在那些元素被指定绑定,你需要命令Knockout获取你的数据并应用到这些元素上,使用ko.applyBindings
函数。
var item = {
id: 88,
name: "Apple Pie"
};
ko.applyBindings(item);
在这里,这个div
会显示"Apple Pie",并且在编辑修改input
时保存到你的item里的name
属性里。可是,这个更新不会导致你的div
自动更新。要使这些更新引起注意,name
属性需要使用Knockout的一个重要架构,observable会创建一个关注点(observable),name
属性需要包到ko.observable
函数里并执行。
var item = {
id: 88,
name: ko.observable("Apple Pie")
};
ko.applyBindings(item);
现在,当name
关注点有变化时这个div
会被更新。数据绑定自动订阅所有关注点,并且每当它们被通知更新时,可以适时更新它们的DOM元素。你也可以在订阅的关注点变化时进行编程,执行额外的逻辑,比如把数据传回服务器。
使用MVVM模式(Model-View-View-Model)
Knockout允许你为你的交互使用MVVM模式架构。这个模式通过保持几个不同层次的分离,描述如何管理复杂的功能。
模型(Model)
这个模型代表你的应用数据。Knockout没有专门处理从后端服务器返回的中间数据的功能,让你来决定使用最好的方法来实现。特定的后端技术对于Knockout并不重要,只要你可以在你的JavaScript代码里访问你的数据。常用的方法有以下几个:
- 使用AJAX请求服务器发送和接收数据
- 序列化数据直接到页面脚本块
- 在适当的时候,使用静态数据,通常从一个外部JavaScript文件加载
- 在某些情况下,甚至从一个呈现的内容创建一个视图模型,使用自定义绑定
视图(View)
view代表你的标识,可以真正被当作一个模板来渲染对应你的视图模型。对于一个健壮的视图模型,视图应真正地获得所有的那些需要使用简单的声明绑定对应的视图模型可见的概念。如果你发现你的绑定需要包含复杂的逻辑或者程序达到正确的结果,那么,这是好的迹象,视图模型没有结构化,在某种意义上这是最好的视图架构。
视图模型(View model)
View model是你的应用的心脏。它是你的用户界面的代码式描述,它包括数据和相关用于处理数据的行为。视图模型的目标是为视图和相应绑定提供一个易于理解的结构。它可能包括过滤(filtered)、排序(sorted),和模型版本的操作数据,还有潜在的额外元数据或概念,涉及到用户界面操作。例如,你可能会跟踪item的可见性和可编辑性,但是不保存这个信息到数据库。
在一个理想的Knockout应用里,你的视图模型包含非引用到DOM元素、选择器(selectors),或任何的绑定视图的知识点。这种分离带来了些不错的优势:
- 视图模型就其本身而言,很容易,不包含UI
- 你可以正常重构你的标识而不必担心破坏选择器。你确定绑定是否仍然合适,但是他们直接列出你正在移动的元素。
- 一个简单的视图模型可以用于多个视图。一个常见的例子是渲染一个简单的item视图,对比处理items集合。在某些情况下,你也可以渲染另一种移动视图,共享相同视图模型代码。
Knockout核心结构
在JavaScript中,设置属性值本身不能通知任何人它被改变了。为了支持这种需求,Knockout创建了几个结构,他们的功能是跟踪订阅和当有变化时执行。当底层数据有变化时,绑定被触发,并适时地更新到DOM元素。
关注点Observables
Knockout的基本结构是方便跟踪和改变,被称为observable。你通过调用ko.observable()
创建observable。一个observable实际上是一个缓存了当前值的函数。要恢复一个observable的值,你可以调用一个不带参的函数。设置一个值,你通过一个简单的参数来为observable设置一个新值:
this.name = ko.observable("Apple Pie");
alert(this.name()); //read the value
this.name("Pumpkin Pie"); //set the value
动态关注点(Computed observables)
Knockout也支持Computed observables,作为一种动态值——当绑定的对象发生改变时,值永远保持更新:
this.formattedName = ko.computed(function() {
return this.id + " - " + this.name();
}, this);
当一个动态observable被创建时,它将缓存它现在的值当它的绑定对象更新时仅重新估算。一个动态observable通过跟踪每一次重新估算得到的值来声明动态绑定。这意味着你不需要为那些值指定更新完成时的绑定,并且估算逻辑仅需要在绑定有变化时才运行。
Observable数组(ObservableArrays)
大多数应用程序处理一个或多个数据集合。Knockout包含ObservableArrays来支持数据数组,当UI需要知道items是被添加或被移除。ko.observableArray
只是一个可见的、包括额外处理普通数组操作的函数。当操作一个ObservableArrays,底层数组被更新,然后通知相关订阅数组已经改变。
this.items = ko.observableArray([
{ id: 1, name: "Apple Pie" },
{ id: 2, name: "Pumpkin Pie" },
{ id: 3, name: "Blueberry Torte" }
]);
this.items.push({ id: 4, "Strawberry Shortcake" });
重要是要注意,ObservableArrays跟踪数组里的item更改,而不是单独的一个item的属性。在上面的例子中,如果你的应用需要跟踪item的name,那么name属性需要也加一个observable。
内置绑定(Built-in bindings)
绑定是神奇的,它关联你的标识与你的视图模型。Knockout包含常见用例的绑定,也允许你使用自定义函数进行扩展。表1-3总结了Knockout支持的绑定:
表1:常用绑定
Binding |
Description |
visible |
The visible binding allows you to show or hide an element based on a value passed to it. |
<div data-bind="visible: hasError">An error has occurred</div>
|
|
Text |
The text binding populates the content of the element with the value passed to it. |
<div data-bind="text: message"></div>
|
|
html |
The html binding populates the children of this element with the markup passed to it. |
<div data-bind="html: markup"></div>
|
|
Css |
The css binding toggles one or more CSS classes on the element. |
<div data-bind="css: { error: hasError, required: isRequired }">content</div>
|
|
style |
The style binding adds style values to the element. |
<div data-bind="style: { color: messageColor, backgroundColor: backColor }">content</div>
|
|
Attr |
The attr binding sets the value of one or more attributes on the element. |
<div data-bind="attr: { title: itemDescription, id: itemId }">content</div>
|
表2: 表单
Binding |
Description |
Click |
The click binding executes a handler when the element is clicked. |
<button data-bind="click: addItem">Add Item</button>
|
|
Event |
The event binding adds handlers to the element for the specified events. |
<div data-bind="event: { mouseover: showHelp, mouseout: hideHelp }">content</div>
|
|
Submit |
The submit binding allows you to execute a handler when a form is submitted. |
<form data-bind="submit: saveData">…</form>
|
|
Value |
The value binding enables two-way binding of the field’s value to a view model value. |
<input data-bind="value: name" />
|
|
Enable |
The enable binding controls whether the form element is enabled passed on the passed value. |
<input data-bind="enable: isEditable, value: name" />
|
|
Disable |
The disable binding provides the same functionality as the enable binding, but uses the opposite of the passed value. |
<input data-bind="disable: isReadOnly, value: name" />
|
|
Hasfocus |
The hasfocus binding tracks the focus state of the element and attempts to give the field focus when the value is set to true. |
<input data-bind="hasfocus: nameFocused, value: name" />
|
|
Checked |
The checkbox binding is used to bind against radio buttons or checkboxes. This can track true or false whether a checkbox is checked, the value of the currently selected radio button, or when bound against an array it can track all of the currently checked values. |
<input type="checkbox" data-bind="checked: isActive" />
|
|
Options |
The options binding is used to populate the options of a select element. It includes optionsText, optionsValue, and optionsCaption options that customize the way that the value is displayed and stored. |
<select data-bind="options: choices, value: name"></select>
|
|
selectedOptions |
The selectedOptions binding tracks the currently selected items for a select element that allows multiple selections. |
<select data-bind="options: availableFilters, selectedOptions: selectedFilters" size="10" multiple="true"></select>
|
表3: 流程控制、模板
Binding |
Description |
If |
The if binding determines if the element’s children are rendered and bound. It takes a copy of the child element to use as a “template” for handling when the bound value changes. |
<div data-bind="if: detailsLoaded"> <div data-bind="text: content"></div> </div>
|
|
ifnot |
The ifnot binding provides the same functionality as the if binding, but uses the opposite of the value passed to it to determine if the element’s should be rendered and bound. |
<div data-bind="ifnot: hideDetails"> <div data-bind="text: content"></div> </div>
|
|
with |
The with binding will bind the child elements using the value passed to it as the data context. It will not render the children if the value is null/undefined/false. It will also retain a copy of the child elements to use as a “template” for handling when the bound value changes. |
<div data-bind="with: details"> <div data-bind="text: title"></div> <div data-bind="text: content"></div> </div>
|
|
foreach |
The foreach binding will use the child elements as a “template” to repeat for each item in the array passed to it. |
<ul data-bind="foreach: items"> <li data-bind="text: name"></li> </ul>
|
|
template |
The template binding provides the underlying functionality for the if, ifnot, with, and foreach bindings, but also allows you to supply a named template that you can reuse multiple times. named template. |
<!—just passing a named template --> <div data-bind="template: ‘itemsTmpl‘"></div> <script id="itemTmpl" type="text/html"> <div data-bind="text: name"></div> </script> <!—controlling the data that is bound by the template --> <div data-bind="template: { name: ‘itemTmpl‘, data: currentItem }"></div> <!—iterating through an array of items --> <div data-bind="template: { name: ‘itemTmpl‘, foreach: items }"></div>
|
|
Knockout扩展
Knockout的指导原则之一就是坚持自己的优点并提供它核心能力之外功能的可扩展。这使得你可以将Knockout作为首选的类库和技术。为了最大化地使用Knockout,你应该了解Knockout提供的各种扩展项。
自定义绑定
Knockout让你轻松地创建自己的绑定,这也是最常用的扩展项。当你需要编写触发两个数据和DOM元素的代码时,使用内置绑定无法做到,那么你可能想创建一个自定义绑定。
所有的绑定(自定义和内置)都源自ko.bindingHandlers
对象。每个绑定是一个对象,可以选择生成一个init的函数,当绑定被第一次被执行时运行,一个updata
函数,当它的绑定有一个或更多发生变化时运行。
这是一个自定义绑定的例子,包含已有的text绑定,当绑定改变时渐隐。
ko.bindingHandlers.fadeInText = {
update: function(element, valueAccessor, allBindings, data, context) {
$(element).hide();
ko.bindingHandlers.text.update(element, valueAccessor);
$(element).fadeIn();
}
};
更多自定义绑定的信息,请看custom bindings documentation(l自定义绑定文档)和custom bindings blog post(博客:自定义绑定),讨论常用自定义绑定案例。
扩展Knockout的核心结构
Knockout也允许你扩展前面讲过的核心结构。这创造了一个有吸引力的机会——创建可重用的功能,可以让你的视图模型代码更干净、简洁。
Knockout的核心类型所有常用的函数都源自他们的fn对象,即ko.observable.fn。所以,任何你添加到ko.observable.fn的函数对所有的observables都是有效的。更多的关于类型层次结构的信息请看Knockout官方网站的Adding custom functions using "fn"(使用 "fn "添加自定义函数)。
下面是一个例子,你创建一个editable
的扩展,它添加一个editValue子可见项(sub-observable)到observable中。这允许你绑定对应的临时值,并通过调用accept
函数将它提交到你的真正的observable中。
ko.observable.fn.editable = function() {
//create a sub-observable that we can bind against for editing
this.editValue = ko.observable(this());
//when accept is called, commit the temp value to our real value
this.accept = function() {
this(this.editValue());
}.bind(this);
//return the observable itself (this) to support chaining
return this;
};
你可以用这个扩展,当你需要创建一个observable,比如:
this.name = ko.observable("Bob").editable();
在你的标识中,你可以绑定对应,比如:
<div data-bind="text: name"></div>
<input data-bind="value: name.editValue" />
<button data-bind="click: name.accept">Accept</button>
Knockout的每一个核心类型的fn
的扩展,对于创建重用是一个实用功能,它允许你扩展Knockout的功能。除了 fn
对象,Knockout也支持一个类似的方法,叫做extenders,它的详细描述在Knockout文档的Using extenders to augment observables(使用extenders扩展observables)。
其他可扩展项
Knockout也包括其他一些值得注意的扩展项。
- 模板母版:Knockout有可供选择使用的模板母版,在template(模板)绑定中。一个基本的模板母版可被扩展,定制一个如何将数据和模板执行声明生成标识达到绑定。
- 模板源码: 你可以定制Knockout如何声明模板主体。这需要你下载模板,来源于外部源码或商店模板。
- 绑定供应:你可以重写Knockout如何声明每一个元素被绑定,和这些绑定如何被转化。这个意味着你可以重置data-bind属性,通过你自己的结构。一个可供选择的绑定供应,它允许你在代码中指定绑定并从已经下载的标识中嵌进去,在我的 knockout-classBindingProvider GitHub project中和 Knockout 1.3 preview blog post 中描述如何建立一个绑定供应。
下一步阅读方向
现在你了解了Knockout的基础,接下来你应该做什么?一个开始的好去处 Knockout在线教程。这些教程讲解各种常见的用处,从基础知识到更高级的资料。另外,全部文档在 Knockout官方网站,涉及到的代码是有效的,来自于 Knockout GitHub project。
KO学习系列:http://blog.darkthread.net/post-2012-05-09-knockout-js-intro.aspx
Knockout.js入门相关推荐
- MVC、MVP、MVVM、Angular.js、Knockout.js、Backbone.js、React.js、Ember.js、Avalon.js、Vue.js 概念摘录...
注:文章内容都是摘录性文字,自己阅读的一些笔记,方便日后查看. MVC MVC(Model-View-Controller),M 是指业务模型,V 是指用户界面,C 则是控制器,使用 MVC 的目的是 ...
- knockout.js简单实用教程1
第一次接触knockout是在一年多之前吧.当时是接手了一个别人的项目,在项目中有用到knockout来进行数据的绑定.也就开始学习起来knockout.在之后的项目中也多次用到了这个.在第一次开始学 ...
- js模板字符串自定义类名_【Vue.js 入门到实战教程】07Vue 组件注册 | 基本使用和组件嵌套...
来源 | https://xueyuanjun.com/post/21929除了前面介绍的基本语法之外,Vue.js 还支持通过组件构建复杂的功能模块,组件可以称得上是 Vue.js 的灵魂,是 Vu ...
- JQuery Ajax 与 Knockout.js的结合
项目中遇到太多JQuery Ajax 与 Knockout.js的结合来处理数据的情况,今天将这部分内容梳理一下.一来弄清楚二者结合处理数据的流程,二来是感觉二者结合在一起用的时候有点将二者搞混了,弄 ...
- React.js入门笔记
# React.js入门笔记 核心提示 这是本人学习react.js的第一篇入门笔记,估计也会是该系列涵盖内容最多的笔记,主要内容来自英文官方文档的快速上手部分和阮一峰博客教程.当然,还有我自己尝试的 ...
- Ember.js入门教程、博文汇总
第一章 对象模型 Ember.js 入门指南--类的定义.初始化.继承 Ember.js 入门指南--类的扩展(reopen) Ember.js 入门指南--计算属性(compute properti ...
- node.js入门 - 9.api:http
node一个重要任务是用来创建web服务,接下来我们就学习与此相关的一个重要的api -- http.我们使用http.createServer()创建一个http服务的实例,用来处理来自客户的请求. ...
- 《Node.js入门》Windows 7下Node.js Web开发环境搭建笔记
最近想尝试一下在IBM Bluemix上使用Node.js创建Web应用程序,所以需要在本地搭建Node.js Web的开发测试环境. 这里讲的是Windows下的搭建方法,使用CentOS 的小伙伴 ...
- 【ASP.NET Web API教程】2.3.5 用Knockout.js创建动态UI
[ASP.NET Web API教程]2.3.5 用Knockout.js创建动态UI 原文:[ASP.NET Web API教程]2.3.5 用Knockout.js创建动态UI 注:本文是[ASP ...
最新文章
- 在中WebBrowser加载Excel后获取excel对象
- jQuery库中的变量$和其它类库的变量$冲突解决方案
- 不懂物理,何以谈科技?
- Problem B: C语言习题 矩阵元素变换
- 比亚迪后续车都会搭在鸿蒙系统吗_华为鸿蒙系统上车,比亚迪汉发布!我告诉你华为鸿蒙到底是什么...
- Spring AOP之XML配置
- 推荐 Word、EXCEL必备工具箱
- 易语言变量和c对应关系,详解易语言变量用法和原理
- 单片机仿真软件proteus8安装与使用
- 升级了谷歌浏览器最新版不习惯,如何降级版本
- Python3网络爬虫(十四):跟股神巴菲特学习炒股之财务报表入库(MySQL)
- jQuery EasyUI教程
- 关于七牛云上传图片的总结
- 深度学习系统 深度学习软件
- 论文阅读三:基于改进人工蜂群算法的SDN负载均衡策略研究
- android 手机 p8 GRA-CL00 无法收到组播问题记录
- 楼道扶手智能清洁装置的制作分享
- Python爬虫入门【7】: 蜂鸟网图片爬取之二
- Kotlin 1.4 和未来值得期待的地方
- Spring MVC学习笔记