Knockout.js入门

来源:互联网 发布:自动打铃软件 编辑:程序博客网 时间:2024/06/06 14:06

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, andoptionsCaption 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 aselect 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 theif, 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

原创粉丝点击