前言前端
什么,你如今还在看knockout.js?这货都已经落后主流一千年了!赶忙去学Angular、React啊,再不赶忙的话,他们也要变out了哦。身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包,却依然振振有词地喋喋不休,一脸真诚。是啊,前端发展太快,那边前几年出的框架已经是无人问津的半老徐娘,而这边各类新出的框架却正在风口搔首弄姿,娇翠欲滴。前端界好不热闹!固然,楼主也喜欢新奇趣,可是如今公司的开发工具 WeX5 中用到了knockout.js,没办法,再老都只能蒙着眼睛上了……json
而后发现,咦!感受还不错呀~~~数组
MVVM框架中Angular是好,但这么大而全的框架,学习难度可不低呢,上手起码也得要个一两周吧。而knockout.js专一于数据绑定,只需一两天就能够投入使用了,学习成本不要过低!在前端进化如此迅速的时代,学习成本也是不得不考虑的一个因素。不少时候其实咱们的项目并没那么复杂,也并不须要万能的框架,更须要的反而是简单顺手的工具。app
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楼主就很少说了,一图以蔽之:
MVVM模式
ko创建在3大核心特征之上(官网介绍):
可观察对象与依赖跟踪 (Observables and dependency tracking):使用可观察对象在模型数据之间设立隐性关系链,用于数据转换和绑定。
声明式绑定 (Declarative bindings):使用简单易读的语法方便地将模型数据与DOM元素绑定在一块儿。
模板 (Templating):内置模板引擎、为你的模型数据快速编写复杂的 UI 展示。
使用ko很是简单,直接到官网下载并用<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表明什么东西呢?从例子能够看到XX为标签的属性,能够是text、value、class、checked等标签属性,其实也能够是click、focus、load等DOM事件。OO看起来像是一个变量,实际上并非变量,而是一个函数对象,执行这个函数(带个())就能获得相应的绑定值。经过XXOO就能够将元素的属性或事件跟js中的函数对象绑定在一块儿,这就是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!
操做可观察对象数组的方法有两类,一类是与原生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中。
关于ko的介绍就到这里了!码字不易,随手点赞哈~