Knockout JS 简单入门

学习 “Knockout JS” 老早就已经在计划之内了,可惜迟迟未动。终于的终于,今天总算肯动了~~ 

Knockout JS 是什么

首先它是一个Javascript库,所以这个东西主要被用在网页上的。

其次,它替我们实现了页面元素和JS对象的双向绑定,这不仅帮我们把JS对象与具体的某个页面元素进行解绑,而且任何一方(页面元素或JS对象)的改变,都会立刻反应到另一方。换句话说,它帮助分离了前台的视图逻辑和业务逻辑。这就是MVVM模式的思想。

上面这样讲显然不够直观,下面举个简单的例子:

以 “hello world” 为例,当页面刚启动的时候输入框中显示的是JS对象的值。我们可以随意修改输入框中的值,下面的标签就会立刻显示你输入的内容,效果如下:

在没有使用 Knockout JS 的时候,我相信大部人的写法类似如下这种(下面的代码基于JQuery,如果你有更好地写法,欢迎指正):

<p> Hello <input type="text"></input></p>
<p> Hello <span></span></p>

<script>
var $input = $("input[type=text]");
var val = "world";
$input.val(val);
$("span").text(val);
$input.blur(function()
{
	val = $input.val();
	$("span").text(val);
});
</script>

上面这种写法是假设页面中只有一个input和span,否则写法还会稍微复杂点。


使用了 Knockout JS 之后,上面的代码就会变成如下的样子:

<p> Hello <input type="text" data-bind="value:name"></input></p>
<p> Hello <span data-bind="text:name"></span></p>

<script>
var viewModel = {name:ko.observable("world")};
ko.applyBindings(viewModel);
</script>


你有看出这两种的区别吗?请在仔细思考后再看我的答案(选中下面两行即可)。

从代码结构上看,第二种方案显然要更干净、简洁

从耦合度上看,第一种方案的JS直接引用了页面元素如input、span,而第二种方案的JS完全没有出现任何页面元素。这样显著的好处就是UI工程师可以更专注于页面布局,而开发工程师更专注于业务逻辑。


准备工作

和使用其它JS库一样,首先你需要在你的项目中添加对 Knockout JS 的引用,如果你开发的是ASP.NET MVC4 应用程序,那么微软已经替你把该库添加到项目中了。


属性绑定

属性绑定就是把变化的内容和元素的属性绑定在一起。想要把JS对象与一个页面元素绑定在一起,需要做2件事情。


1、修改页面元素,添加 “data-bind”

只需要在页面元素中添加 "data-bind" 属性,而不需要 ID 和 class。比如上面的示例中:

<span data-bind="text:name"></span>

data-bind="text:name" 中的 text 表示我们希望绑定到 span 的 text 属性上。如果是 input[type=text],那么 text 应该用 value 代替,如果是 input[type=checkbox],那就该用 checked 代替。

name 可以理解成一个占位符(一个变量),最终会被 JS 对象中的值给替换掉。


2、编写 JS 代码,添加绑定

既然使用是MVVM的方式,就需要创建一个 ViewModel 来承载页面元素的值。

var viewModel = { name:"world"}; //这个 name 必须和 data-bind 中所指定的占位符一样。

有了 viewModel 后,就需要把这个 viewModel 和页面关联起来,这个事情只需要一句话:

ko.applyBindings(viewModel); //ko 是 Knockout 的对象,只要把 knockout 的库添加到页面中,就可以直接访问该对象。


有细心的童鞋可能发现问题了,这里 viewmodel 给了一个固定值。这种情况相当于赋值操作,而且只在第一次执行的时候生效,并不会跟踪该值的变化。


3、修改 JS 代码,使支持 observable

Observable 是整个 Knockout 的关键,启用 Observable 就会启用跟踪,这样不管哪一方修改值,都会通知 Knockout,从而告诉另一方进行修改。这才真正实现了双向绑定。

var viewModel = { name:ko.observable("world")};

现在,如果你再尝试在页面运行后修改输入框的值,ViewModel的值就会随之改变,你也可以通过修改 JS 对象的值,来改变页面的显示,修改值的方式如下:

viewModel.name("honey"); //这里的name不再是属性,而是一个方法。只有通过这种方式才能通知 knockout 。


通过以上三步,就可以完成一个最简单的MVVM的页面例子。


事件绑定

页面元素的事件也可以通过 Knockout 进行绑定。同样来看一个最简单的示例,点击 click 按钮会弹出 “Hello world” 对话框。

<button data-bind="click:sayHello" >click</button>

<script>
var viewModel = { sayHello:function(){ alert("hello world");}};
ko.applyBindings(viewModel);
</script>


数组绑定

除了上述绑定,接着来看一个相对麻烦的数组绑定。数组在页面上最常见的表现形式就是下拉列表,我就用此来做演示。


显示下拉列表

<select data-bind="options:persons,
		   optionsText:'name',
		   optionsCaption:'Choose...' >
</select>

<script>
var viewModel = {
	persons:[{name:"Charley"},{name:"Lavender"},{name: "Arron"}]
};
ko.applyBindings(viewModel);
</script>

如果你已经对属性绑定比较熟悉,那上面这种表示方式肯定不会陌生。viewModel是一个最普通的 JS 数组对象,data-bind 当中的 options 用于指示数组元素的集合,optionsText 因为数组元素是一个JS对象,所以要通过 optionsText 来指定最终将被显示在页面上属性名,optionsCaption 表示默认显示在下拉框中的文字。


显示被选中的列表项

要想显示当前选中项,就必须监视 select 元素的 value,于是把修改上面的代码:

<select data-bind="options:persons,
		   optionsText:'name',
		   optionsCaption:'Choose...',
		   value:selectedPerson" >
</select>
<!-- with 用于表示当前绑定的元素与子孙元素之前存在嵌套关系 -->
<p data-bind="with: selectedPerson">
    <span data-bind="text:name"></span>
</p>
<script>
var viewModel = {
	persons:[{name:"Charley"},{name:"Lavender"},{name: "Arron"}],
	selectedPerson: ko.observable()
};
ko.applyBindings(viewModel);
</script>

在 select 元素的 data-bind 中加上对 value 的绑定,这样就把 selectedPerson 和 value 绑定在了一起,然后再在 viewModel 中添加对 selectedPerson 的监视从而实现双向绑定。


添加/删除数组元素

要想知道数组元素的变化,就得对整个数组进行监视,Knockout 提供了 observableArray 来实现数组的监视。

<input data-bind="value:newbie" type="text"/><button data-bind="click:addPerson">Add</button>
<select data-bind="options:persons,
		   optionsText:'name',
		   optionsCaption:'Choose...',
		   value:selectedPerson">
</select>
<p data-bind="with: selectedPerson">
<span data-bind="text: name"></span>
</p>
<script>
var viewModel = function(){
	this.persons        = ko.observableArray([{name:"Charley"},{name:"Lavender"},{name: "Arron"}]); //<--  使用 observableArray
	this.selectedPerson = ko.observable();
	this.newbie         = ko.observable();
	this.addPerson      = function()
	{
		this.persons.push({name:this.newbie}); //和普通的数组操作方法一样, 删除的话直接调用 this.persons.pop();
	}
};
ko.applyBindings(new viewModel());
</script>


关于数组更多的操作请参考官方网站上 Observable Arrays 一节。


流程控制

foreach

对于数组而言,除了常用的下拉列表这种方式,有些时候我们还会使用 ul、ol 这些序列来表示,这个时候就会有很多重复的代码(比如多个 li)。foreach 便可以为我们避免这些重复的代码。

<input data-bind="value:newbie" type="text"/><button data-bind="click:addPerson">Add</button> 
Total: <span data-bind="text:persons().length"></span>

<ul data-bind="foreach:persons">
	<li><span data-bind="text: name"/></li> <!-- 这行代码将被重复显示,通过 name 来显示不同的值 -->
</ul>

<script>
var viewModel = function(){
	this.persons        = ko.observableArray([{name:"Charley"},{name:"Lavender"},{name: "Arron"}]);
	this.selectedPerson = ko.observable();
	this.newbie         = ko.observable();
	this.addPerson      = function()
	{
		this.persons.push({name:this.newbie()}); 
	}
};
ko.applyBindings(new viewModel());
</script>

Gif动画演示,请点击放大后观看


关于 foreach 更详细的用法请参考官网 foreach 一节。


if

条件分支可能是使用最多的流程控制语句了。下面的示例将演示当人员数量超过3时显示 “The total number larger than 3 ”字样。语法为 “if: + 表达式”。

<input data-bind="value:newbie" type="text"/><button data-bind="click:addPerson">Add</button> <button data-bind="click:removePerson">Remove</button>
Total: <span data-bind="text:persons().length"></span>

<div data-bind="if: persons().length > 3"> <!-- if: + 表达式 -->
  the total number larger than 3.
</div>
<ul data-bind="foreach:persons">
	<li><span data-bind="text: name"/></li> 
</ul>
<script>
var viewModel = function(){
	this.persons        = ko.observableArray([{name:"Charley"},{name:"Lavender"},{name: "Arron"}]);
	this.selectedPerson = ko.observable();
	this.newbie         = ko.observable();
	this.removePerson   = function()
	{
		this.persons.pop();
	};
	this.addPerson      = function()
	{
		this.persons.push({name:this.newbie()}); 
	}
};
ko.applyBindings(new viewModel());
</script>


上述简单的判断分支可以直接用属性绑定的方式代替:

<div data-bind="visible:persons().length > 3">
  the total number larger than 3.
</div>

对于属性是布尔类型的可以直接通过这种方式绑定,与之类似的还有 enable、checked 等。


关于 if 更详细的用法请参考官网 if 一节。


总 结

本文只是简单地介绍了下 Knockout JS 的一些基本功能,利用 Knockout JS,我们就可以让我们的前台代码更整洁更易维护和扩展。


参考资源

    官方网站文档

    Knockout.js入门


文章索引

[隐 藏]

本站采用知识共享署名 3.0 中国大陆许可协议进行许可。 ©2014 Charley Box | 关于本站 | 浙ICP备13014059号