前言

什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular、React啊,再不赶紧的话,他们也要变out了哦。身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包,却依然振振有词地喋喋不休,一脸真诚。是啊,前端发展太快,那边前几年出的框架已是无人问津的半老徐娘,而这边各种新出的框架却正在风口搔首弄姿,娇翠欲滴。前端界好不热闹!当然,楼主也喜欢新奇趣,但是现在公司的开发工具(WeX5)中用到了knockout.js,没办法,再老都只能蒙着眼睛上了……

然后发现,咦!感觉还不错呀~~~

MVVM框架中Angular是好,但这么大而全的框架,学习难度可不低呢,上手起码也得要个一两周吧。而knockout.js专注于数据绑定,只需一两天就可以投入使用了,学习成本不要太低!在前端进化如此迅速的时代,学习成本也是不得不考虑的一个因素。很多时候其实我们的项目并没那么复杂,也并不需要万能的框架,更需要的反而是简单顺手的工具。

好东西就要和大家分享(妹子、票子除外!),于是小茄也基于官方教程写了个knockout.js的使用教程,加入了自己的个人体会,所有demo均基于目前knockout.js的最新版本(3.4.0)。小茄才识有限,文中若有不当之处,还望大家指出。

目录:

【Knockout.js 学习体验之旅】(1)ko初体验

【Knockout.js 学习体验之旅】(2)花式捆绑

Before Knockout.js

假设我们做一个订单系统,需要显示商品单价,然后可以根据输入数量计算出总价并显示出来。使用原生代码也很容易实现,效果:

代码如下:

<!--HTML code-->
Price: <span id="price"></span><br />
Account: <input type="text" id="account" value="" placeholder="请输入数量" /><br />
sum: <span id="sum"></span>
//js code
var priceNode = document.getElementById('price'),
accountNode = document.getElementById('account'),
sumNode = document.getElementById('sum'),
price = 100,
account = 11,
sum = price * account; //初始化。
priceNode.innerText = price;
accountNode.value = account;
sumNode.textContent = sum; //监视 View层的用户输入
accountNode.addEventListener('keydown', function (e) {
window.setTimeout(function () {
account = accountNode.value;
sum = price * account;
sumNode.textContent = sum;
},10);
});

嗯,蛮简单的!哦,对了,我们一次展示50件商品,同时又有10类这样的展示,还有买5盒冈本送一根油条这样的各种促销……

所以,你知道原生实现的问题了吧:

  • 随着 UI 和数据交互的增多,代码量迅速增长,难以维护
  • 基于 Dom 查询,id 或 class 的命名难以管理
  • 代码耦合度高,难以复用

Knockout.js简介

Knockout.js(下面简称ko)就是为了解决上述问题而出现的,他是一个轻量级的MVVM库,专注于实现数据与视图的绑定,本身并不提供 UI 类和路由等功能,上手非常快。同时,由于ko出来已经有些年头了,已经是比较成熟的框架了。在做一些动态显示比较多的页面时,ko无疑是一个比较好的选择。关于MVVM楼主就不多说了,一图以蔽之:

ko建立在3大核心特征之上(官网介绍):

1. 可观察对象与依赖跟踪 (Observables and dependency tracking):使用可观察对象在模型数据之间设立隐性关系链,用于数据转换和绑定。

2. 声明式绑定 (Declarative bindings):使用简单易读的语法方便地将模型数据与DOM元素绑定在一起。

3. 模板 (Templating):内置模板引擎、为你的模型数据快速编写复杂的 UI 展现。

使用ko非常简单,直接到官网(http://knockoutjs.com/index.html)下载并用<script>引入即可。

可观察对象

使用ko重写上面的例子(自定价格,这也是我小时候的愿望之一):

代码是这样的:

<!--HTML Code-->
<div id="one">
Price: <input type="text" data-bind="value: price" placeholder="请输入单价" /><br />
Account: <input type="text" data-bind="value: account" placeholder="请输入个数" /><br />
sum: <span data-bind="text: sum"></span>
</div>
// js Code
var ViewModel = function(p, a) {
//设置为可观察对象并以参数p、a初始化
this.price = ko.observable(p);
this.account = ko.observable(a);
//调用ko函数的时候将this传入,否则执行ko.pureComputed内部代码时,this为ko,ko.price()报错。
this.sum = ko.pureComputed(function() {
//因为可观察对象是一个函数对象,所以要用 price()来读取当前值。
//设置值使用price(NewValue),支持链式写法:this.price(12).account(3)
return this.price() * this.account();
}, this);
};
var vm = new ViewModel(135, 10);
//应用该绑定,绑定开始生效
ko.applyBindings(vm);

1)先看HTML代码:

可以看到在每个标签中都加入了一个 data-bind = "XX:OO" 这样的键-值对。这个就是 ko 的绑定语法,XXOO代表什么东西呢?(XXOO?楼主还是个孩子啊…)从例子可以看到XX为标签的属性,可以是text、value、class、checked等标签属性,其实也可以是click、focus、load等DOM事件。OO看起来像是一个变量,实际上并不是变量,而是一个函数对象,执行这个函数(带个())就能得到相应的绑定值。通过XXOO就可以将元素的属性或事件跟js中的函数对象绑定在一起(XXOO了就要相互负责哈),这就是ko的声明式绑定。绑定的定义其实就是一个观察者模式,只不过这是双向的绑定,发布者和订阅者相互订阅了对方的消息而已,这就是MVVM的双向绑定。ko双向绑定的结果就是一方变化就可以自动更新另一方,也就是通过ViewModel将数据和表现层紧紧绑定在一起了。绑定的效果类似于:

2)再看看js代码:

可以看到js中定义了一个ViewModel对象,在对象中对HTML中绑定的OO进行了操作。这里主要有两个操作: ko.observable()和ko.pureComputed()。

  • ko.observable(p):见名知义、这个就是设置可观察对象的方法,传入的参数p就是初始化的值,这里的参数可以是基本数据类型,也可以是一个json对象。被设置为可观察对象后就意味着系统会一直观察这个值。无论是ViewModel中的p还是被绑定对象的p发生变化都会引起刷新事件,将所有用到这个值的地方都更新到最新状态。显然,可观察对象是比较消耗性能的,所以对于不需要动态变更的值(如价格)则不要设置为可观察对象,当然还是需要放入ViewModel中进行集中初始化。
  • 注意:ko.observable(p)返回的可观察对象是一个函数对象,所以读取可观察对象需要使用price()这种方式;同样的,设置可观察对象需要使用price(newValue)这种方式。比较贴心的是,设置的时候支持链式写法:ViewModel.price(100).account(10)。
  • ko.pureComputed()就是所谓的依赖跟踪了,这里是单价*数量等于总价,注意这里不能直接用this.sum = this.price() * this.account();来指定sum,这种写法不能动态刷新被绑定的对象,只是动态改变了sum变量,但要去刷新绑定对象还需要其他操作。所以,与计算相关的绑定值都要用ko的计算函数来设置。当然,返回的也是一个函数对象。另外,ko还有一个computed函数,也可以用其来进行设置,不过推荐使用pure的方式,以提高性能。
  • 注意这里的写法:ko.pureComputed(fn, this),也就是将fn绑定到ViewModel作用域中,其实就是js中的call/apply。因为在执行ko内部函数的时候,this为ko对象,所以为了得到ViewModel对象的作用域,需要通过上面的写法传入this。当然也可以在ko函数外部用that保存ViewModel对象,然后在ko函数内部使用that来调用ViewModel对象。像这样:
var that = this;
this.sum = ko.pureComputed(function() {
return that.price() * that.account();
});

定义好ViewModel构造函数后便实例化了一个ViewModel对象,然后使用了ko.applyBindings()的方式来使得绑定生效,这一步不要漏掉了。

使用ko的页面简单模式:

<!--HTML Code-->
<span data-bind="text: bindtext"></span>
// js Code
var viewModel = {
bindtext: ko.observable('initValue')
};
ko.applyBindings(viewModel);

总结起来就是:HTML中使用data-bind="XX: OO"声明绑定,js中建立ViewModel并设置可观察对象,最后应用绑定。

可观察对象数组

再看看可观察对象数组的使用方法,在ko中可不能像js一样数组和变量混用,对于数组对象就要用ko.observableArray([…,…])这种形式,同样的,数组元素也可以是基本类型也可以是json对象。ko中的可观察对象数组有一系列的数组操作方法,如slice()、sort()、push()这种,效果跟原生的js数组操作方法一样,只是通过ko方法所做的改动会通知到订阅者从而刷新界面,但js方法则不会刷新界面。下面是一个简单例子:

<!--HTML Code-->
<select data-bind="options: list"></select>
// js Code
var vm = {
// list: ko.observableArray()
list: ko.observableArray(['Luffy','Zoro','Sanji'])
};
ko.applyBindings(vm);

关键点:ko监控的是数组的状态,而不是元素本身的状态。也就是说当数组状态变化(增减元素)的时候会触发ko事件引起绑定对象的刷新,但数组内部元素的变化(如:值变化)则不被监控不能触发ko事件。例如:

在控制台中使用原生方法将Luffy动态改成Lucy是不会刷新UI页面的,而使用ko的数组操作改动数组则会立即刷新页面,值得注意的是在刷新的时候,也会将之前的改动刷新出来(Luffy > Lucy)。也就是说其实js内存中的变量是已经改变了,但是还缺少一个刷新DOM的动作。这里大家可以看到,读取数组的方法是vm.list()[0],因为list也是一个函数对象,执行返回值才是我们想要的list内容。同理,也可以通过 vm.list(["妹子","妹子","妹子"]) 这样的方式重置可观察对象数组,也能立即刷新UI。

如果需要将数组元素的改动也动态反应到UI上,需要将数组元素也设置为可观察对象,然后使用ko的方法改变数组元素值。注意,是使用ko的方法 list()[0]("Lucy")!

操作可观察对象数组的方法有两类,一类是与原生js数组方法同名的:pop, push, shift, unshift, reverse, sort, splice,这一部分与js原生方法的用法和效果都一样,就不再赘述了。

另外一些方法是js中没有的,主要有以下几个:

  • remove(someItem) -- 删除所有值与someItem相等的元素项并将它们以数组形式返回,这里的意思就是说你可不能直接list.remove(0)来删除第一项,而是要用list.remove(list()[0]) 这种形式来删除。总而言之,传入的参数必须是元素项的值,可以用list()[0] 的形式,也可以直接输入值的字符串(比如“Luffy”这种)。
  • remove(function(item) { return item.age < 18;}) -- 删除所有age属性小于18的元素项并将它们以数组形式返回,这种用法跟平常的数组高阶函数没什么区别。Item作为高阶函数的参数传入,遍历数组时,当高阶函数返回值为真值时就删除该项,否则转到下一项。
  • removeAll(['Chad', 132, undefined]) -- 删除所有值与 'Chad' 或 123 或 undefined 相等的元素项并将它们以数组形式返回。
  • removeAll() -- 删除所有项并以数组形式返回。

小窍门:在处理可观察对象时,若对象数量众多而且交互频繁的情况下,每次变更都立即刷新的话会非常消耗性能,这个时候可以使用扩展 myObservableArray.extend({ rateLimit: 1000 }) 来设置延迟刷新。比如在不断往可观察对象数组中插入元素时,可以设置一个周期时间1000ms,让1000ms内的所有操作集中到一次刷新中去,避免频繁操作 DOM 带来的性能恶化。

总结

本篇主要简单介绍了knockoutjs中最重要的概念:可观察对象(数组)。可观察对象实质上是一个函数对象,通过ko方法操作可观察对象时可以动态刷新UI展现,这个是推荐做法。同时,也可以通过原生的js方法操作可观察对象,只是原生方法并不会刷新UI展现,需要等到下一次刷新事件时才会被刷新到UI中。

关于可观察对象的介绍就到这里了,下一篇 再见!码字不易,随手点赞哈~

参考资料:

  1. 官方教程: http://knockoutjs.com/documentation/introduction.html
  2. 大叔教程(翻译官方的,版本太老):http://www.cnblogs.com/TomXu/archive/2011/11/21/2257154.html
  3. 深入浅出Knockoutjs: http://www.w2bc.com/Article/25175

文字较多,惯例凑图~

原创文章,转载请注明出处!本文链接:http://www.cnblogs.com/qieguo/p/5552580.html 

【Knockout.js 学习体验之旅】(1)ko初体验的更多相关文章

  1. 【Knockout.js 学习体验之旅】(3)模板绑定

    本文是[Knockout.js 学习体验之旅]系列文章的第3篇,所有demo均基于目前knockout.js的最新版本(3.4.0).小茄才识有限,文中若有不当之处,还望大家指出. 目录: [Knoc ...

  2. 【Knockout.js 学习体验之旅】(2)花式捆绑

    本文是[Knockout.js 学习体验之旅]系列文章的第2篇,所有demo均基于目前knockout.js的最新版本(3.4.0).小茄才识有限,文中若有不当之处,还望大家指出. 目录: [Knoc ...

  3. Knockout.Js学习目录

    1.Knockout.Js(简介) 2.Knockout.Js(监控属性Observables) 3.Knockout.Js(属性绑定) 4.Knockout.Js(事件绑定) 5.Knockout. ...

  4. Android开发学习之路--百度地图之初体验

    手机都有gps和网络,通过gps或者网络可以定位到自己,然后通过百度,腾讯啊之类的地图可以显示我们的地理位置.这里学习下百度地图的使用.首先就是要申请开发者了,这个详细就不多讲了.http://dev ...

  5. 关于 knockout js 学习中的疑问 (1)

    最近刚刚学习knockout中遇到如下问题: 1.在给viewModel定义一个方法时,有时后面跟 的this,有的时候没有 如下所示: this.fullName = ko.computed(fun ...

  6. Android开发学习之路--数据持久化之初体验

    上班第一天,虽然工作上处于酱油模式,但是学习上依旧不能拉下,接着学习android开发吧,这里学习数据持久化的 知识. 其实数据持久化就是数据可以保存起来,一般我们保存数据都是以文件,或者数据库的形式 ...

  7. Mono for Andriod学习与实践(1)— 初体验

    对于Andriod的开发者来说,相信Java语言是第一选择,可是对于.Net开发者来说,要想利用C#在Andriod平台上开发,Mono提供了相应的开发平台来实现,Mono for Andriod就是 ...

  8. Web开发学习之路--Springmvc+Hibernate之初体验

    本来想继续学习android的,可是用到了android和服务器交互,需要实现个login的功能,苦于没有这么个环境,那就只能自己来搭建了.既然已经基本上可以玩web了,那么接下来使用web开源的框架 ...

  9. Android开发学习之路--Content Provider之初体验

    天气说变就变,马上又变冷了,还好空气不错,阳光也不错,早起上班的车上的人也不多,公司来的同事和昨天一样一样的,可能明天会多一些吧,那就再来学习android吧.学了两个android的组件,这里学习下 ...

随机推荐

  1. Apache Ignite高性能分布式网格框架-初探

    Apache Ignite初步认识 今年4月开始倒腾openfire,过程中经历了许多,更学到了许多.特别是在集群方面有了很多的认识,真正开始认识到集群的概念及应用方法. 在openfire中使用的集 ...

  2. ExtJS 4.2 组件介绍

    目录 1. 介绍 1.1 说明 1.2 组件分类 1.3 组件名称 1.4 组件结构 2. 组件的创建方式 2.1 Ext.create()创建 2.2 xtype创建 1. 介绍 1.1 说明 Ex ...

  3. javascript工厂模式和构造函数模式创建对象

    一.工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程(本书后面还将讨论其他设计模式及其在JavaScript 中的实现).考虑到在ECMAScript 中无法创 ...

  4. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  5. submit text3常用快捷键

    在网上找了一些submit text的快捷键: Ctrl+D 选词 (反复按快捷键,即可继续向下同时选中下一个相同的文本进行同时编辑)Ctrl+G 跳转到相应的行Ctrl+J 合并行(已选择需要合并的 ...

  6. Autofac - MVC/WebApi中的应用

    Autofac前面写了那么多篇, 其实就是为了今天这一篇, Autofac在MVC和WebApi中的应用. 一.目录结构 先看一下我的目录结构吧, 搭了个非常简单的架构, IOC(web), IBLL ...

  7. C#使用Aspose.Cells导出Excel简单实现

    首先,需要添加引用Aspose.Cells.dll,官网下载地址:http://downloads.aspose.com/cells/net 将DataTable导出Xlsx格式的文件下载(网页输出) ...

  8. java时间

    Calendar.getInstance().getTime() 获取当前时间(包括星期和时区 CST China Standard Time):  Fri Jan 06 21:03:36 CST 2 ...

  9. 手把手教你做个人 app

    我们都知道,开发一个app很大程度依赖服务端:服务端提供接口数据,然后我们展示:另外,开发一个app,还需要美工协助切图.没了接口,没了美工,app似乎只能做成单机版或工具类app,真的是这样的吗?先 ...

  10. jenkins无法重启tomcat的原因

    在使用Hudson的执行sh脚本的时候,如果sh脚本是一个后台进程,如 Tomcat 这样的服务.如果使用Hudson的默认配置,会发现这些sh 进程有启动的过程,但是不会常驻后台,看Hudson 输 ...