Knockoutjs 学习

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

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

目录:

【Knockoutjs 学习体验之旅】(1)ko初体验

【Knockoutjs 学习体验之旅】(2)花式捆绑

Before Knockoutjs

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

代码如下:

<!--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 的命名难以管理
  • 代码耦合度高,难以复用

Knockoutjs简介

Knockoutjs(下面简称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
时间: 2024-10-26 18:03:25

Knockoutjs 学习的相关文章

knockoutJS学习笔记01:从拼接字符串到编写模板引擎

开篇 关于knockout的文章,园里已经有很多大神写过了,而且都写得很好.其实knockout学习起来还是很容易的,看看官网的demo和园里的文章,练习练习就可以上手了(仅限使用,不包含研究源码).之所以想写这个系列,主要是想记录自己的学习和应用过程,也希望能给初学者一点帮助. 既然是学习过程就一步一步来,从最开始的解决方案,到优化过程,到最后的实现方案.有了思考和对比,才会更加明白这个东西有什么好处,为什么使用它.什么情况要使用它.ok, 官网学习链接为?:knockoutJS 准备例子 过

knockoutjs学习笔记:显示绑定

从何而来 本系列摘抄自knockoutjs官方文档,同时结合自身项目使用记录心得. 目标 knockoutjs实现CSS显示的绑定 例子 <div data-bind="visible: shouldShowMessage"> You will see this message only when "shouldShowMessage" holds a true value. </div> <script type="text

knockoutjs学习笔记:文本(Text)绑定

目标 主要用于显示行内元素(<span>,<em>)的内部文本,当然也可用于其他元素. 例子 Today's message is: <span data-bind="text: myMessage"></span> <script type="text/javascript"> var viewModel = { myMessage: ko.observable() // Initially blank

KnockoutJS学习笔记10:KonckoutJS foreach绑定

KnockoutJS foreach绑定用来处理数组,通常用来将一个数组绑定到一个列表或者table中.在foreach绑定中,我们可以使用if.with等嵌套绑定. 示例代码: <table> <thead> <tr><th>First name</th><th>Last name</th></tr> </thead> <tbody data-bind="foreach: peop

KnockoutJs学习笔记(九)

由于component binding部分的内容更为复杂一些,所以这部分我暂时跳过,先学习click binding部分. click binding不仅可以作用于button.input.a等元素,也可以作用于其他任何可见的DOM元素. 下面是一个简单的例子: html部分: 1 <div> 2 You've clicked <span data-bind="text: numberOfClicks"></span> times 3 <but

knockoutJS学习笔记06:ko数组与模板绑定

前面已经介绍了基本的绑定和模板相关知识,接下来就看ko里的数组和模板绑定,数组和模板绑定应该是实际项目中用得比较多的,ko提供了很好的支持. 一.observaleArray 前面的监控属性都是单个对象,用的是ko.observable:有时候后台返回的是一个列表,也就是数组,这个时候就需要用监控数组了.监控数组与监控属性几乎一样,只不过它是一个数组对象,拥有数组的特点.例如:创建一个简单的监控数组: var arr = ko.observableArray(); 也可以开始就进行初始化: ar

knockoutJS学习笔记02:jsRender模板引擎

上一篇最后提到了模板,并尝试自己编写一个最简单版本:有些朋友可能用过 jqtmpl,这是一个基于jquery的模板引擎,不过它已经不再更新了,而且据说渲染速度比较慢.这里介绍另外一个模板引擎:jsRender.个人觉得这些东西学习起来还是很简单的,挑一个看看就行,实际要用到了哪个,官网看看demo也就会用了.之所以选择jsRender,因为它具有以下特点: 简单直观 功能强大 可扩展的 快如闪电 当然,谁都会自己给自己的产品下这样定义.不过我用完后,确实发现它:简单直观.功能强大.扩展性强:至于

KnockOutJS学习系列----(一)

原文地址:http://www.cnblogs.com/n-pei/archive/2011/12/23/2299217.html 好几个月没去写博客了,最近也是因为项目紧张,不过这个不是借口,J. 很多时候可能是因为事情一多,然后没法静下来心来去写点东西,学点东西. 也很抱歉,突然看到好多的短消息,真不知道该如何给大家回复... 最近试着晚上抽时间写一些knockoutjs和mvc的文章.这里先写一点knockoutjs的东西. 关于knockoutjs到底是什么,如果你不知道,可以看看几个月

knockoutJS学习笔记03:knockout简介

通常来说,前端的维护难度是比较大的,特别是脚本,虽然像jquery这样的库可以帮助我们减少很多代码,但在稍微复杂的情况下,还是会产生有很多代码.上一篇介绍了模板引擎jsRender,它可以帮我们快速生成html,减少代码的编写,增强代码的可阅读性和可维护性.但最后也说到,它还是不够强大. 一.mvvm 说到mvvm,就想到以前学过的wpf,虽然学得一般...,对比winform,wpf 里面有很多很好的设计和想法,标签控件.数据驱动.依赖属性... .更关键的是,它是以数据为核心,通过数据驱动U