(一)Knockout 计算属性
Computed Observables
如果您有一个用于firstName
的 observable 对象,和一个用于lastName
的可观察对象,并且您想显示全名,该怎么办?这就是计算可观察性的用武之地——这些函数依赖于一个或多个其他可观察性,并且当这些依赖关系中的任何一个发生变化时,都会自动更新。
例如,给定以下视图模型类,
function AppViewModel() {
this.firstName = ko.observable('Bob');
this.lastName = ko.observable('Smith');
}
您可以添加一个 computed observable 来返回全名:
function AppViewModel() {
// ... 让 firstName 和 lastName 不变 ...
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
}
现在您可以将UI元素绑定到它,例如:
The name is <span data-bind="text: fullName"></span>
1.1 依赖链是有效的
当然,如果您愿意,您可以创建整个 computed observables 链。例如,你可能有:
- 表示一组
items
的 observable 的称为项的 items - 另一个 observable 称为
selectedIndexes
,它存储用户选择的项索引 - 一个名为
selectedItems
的 computed observable,返回与所选索引对应的项对象数组 - 另一个computed observable,返回
true
或false
,这取决于selectedItems
是否具有某些属性(如new或unsaved)。一些UI元素,比如按钮,可能会基于这个值启用或禁用。
对 items
或selectedIndexes
的更改将波及computed observables链,而computed observables链又将更新绑定到它们的任何UI元素。
1.2 管理 ‘this’
给ko.computed
的第二个参数(我们在上面的例子中通过this在哪里)在计算 computed observable 时定义了this
值。如果不传递它,就不可能引用this.firstName()
或this.lastName()
。经验丰富的JavaScript程序员会认为这是显而易见的,但如果您仍然在学习JavaScript,这可能看起来很奇怪。(像c#和Java这样的语言从不期望程序员为此设置值,但是JavaScript会,因为它的函数本身在默认情况下不属于任何对象。)
一种简化事物的流行惯例
也有一种通用的简化方式,即将this在构造函数一开始就赋给另一个变量,这样在之后需要用到this的部分即可以通过调用另一个变量来实现:
function MyViewModel() {
var self = this;
self.firstName = ko.observable("Chiaki");
self.lastName = ko.observable("Izumi");
self.fullName = ko.computed(function() {
return self.firstName()+ " " + self.lastName();
});
}
个人分析,之所以能够通过这种方式来简化,跟javascript中的this机制有关,可能在javascript中每当遇到一个this的时候就分析当前的object到底是哪个,进而对this进行替代,但是进入到ko.computed函数里面之后,由于函数并不算是object的一部分,this的值也就不再是当前的object(MyViewModel),而变成了window,使用self以后就涉及到闭包的问题了,使得self的值并不会更改,这个可以留作以后研究。
1.3 Pure computed observables
如果computed observable知识基于一些observable的简单计算的话(其求值器不直接修改其他对象或状态),使用pureComputed
会比computed更好,如下:
self.fullName = ko.pureComputed(function() {
return self.firstName()+ " " + self.lastName();
})
1.4 强制计算的监控对象始终通知订阅者
我们也可以对computed或是pureComputed进行强制订阅,如下:
当 computed observable 返回一个基本值(数字、字符串、布尔值或null)时,通常只有当值实际更改时才会通知可观察对象的依赖关系。
this.fullName = ko.pureComputed(function() {
return this.firstName() + " " + this.lastName();
}, this).extend({notify: "always"});
1.5 延迟和/或禁止更改通知 rateLimit
同样的,可以通过调用extend方法中的rateLimit属性来指定响应的延时。
// Ensure updates no more than once per 50-millisecond period
myViewModel.fullName.extend({ rateLimit: 50 });
1.6 确定一个属性是否是计算监控的 isComputed
for (var prop in myObject) {
if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop])) {
result[prop] = myObject[prop];
}
}
在某些时候,我们可能需要判定某个变量到底是不是computed observable,这时可以用到ko.isComputed来进行判断,类似的方法还包括isObservablem,isWritableObservable等,其中
- isObservable对于observables、observable arrays、computed observables均会返回true;
- isWritableObservable对于observables、observable arrays、writable computed observables均会返回true,这部分可以留作以后研究。
1.7 当计算监控属性(computed observables)仅在UI中使用时
参考:
官网,computedObservables
CharlieYuki,KnockoutJs学习笔记(三),Using computed observables
2 可写计算监控属性 Writable computed observables
初学者可能希望跳过这一节——可写的计算监控属性是相当高级的,在大多数情况下是不必要的。
通常,计算监控属性的值是从其他监控属性计算出来的,因此是只读的。那么,可能看起来令人惊讶的是,使计算出的监控属性数据可写是可能的。您只需要提供您自己的回调函数,它对写入的值执行一些合理的操作。
您可以使用与常规可写可计算可观察对象完全一样的可写可计算可观察对象,并使用您自己的自定义逻辑拦截所有的读和写。与observables一样,您可以使用链接语法将值写入模型对象上的多个可观察或计算的可观察属性。例如:
myViewModel.fullName('Joe Smith').age(50).
例1:分解用户的输入
返回到经典的“first name + last name = full name” 例子上,你可以让事情调回来看: 让依赖监控属性fullName可写,让用户直接输入姓名全称,然后输入的值将被解析并映射写入到基本的监控属性firstName和lastName上:
var viewModel = {
firstName: ko.observable("Planet"),
lastName: ko.observable("Earth")
};
viewModel.fullName = ko.dependentObservable({
read: function () {
return this.firstName() + " " + this.lastName();
},
write: function (value) {
var lastSpacePos = value.lastIndexOf(" ");
if (lastSpacePos > 0) { // Ignore values with no space character
this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"
this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"
}
},
owner: viewModel
});
这个例子里,写操作的callback接受写入的值,把值分离出来,分别写入到“firstName”和“lastName”上。 你可以像普通情况一样将这个view model绑定到DOM元素上,如下:
<p>First name: <span data-bind="text: firstName"></span></p>
<p>Last name: <span data-bind="text: lastName"></span></p>
<h2>Hello, <input data-bind="value: fullName"/>!</h2>
这是一个Hello World 例子的反例子,姓和名都不可编辑,相反姓和名组成的姓名全称却是可编辑的。
上面的view model演示的是通过一个简单的参数来初始化依赖监控属性。你可以给下面的属性传入任何JavaScript对象:
- read — 必选,一个用来执行取得依赖监控属性当前值的函数。
- write — 可选,如果声明将使你的依赖监控属性可写,别的代码如果这个可写功能写入新值,通过自定义逻辑将值再写入各个基础的监控属性上。
- owner — 可选,如果声明,它就是KO调用read或write的callback时用到的this。查看“管理this”获取更新信息。
例2:全选/反选
当向用户显示一个可选择项列表时,包含一个选择或取消选择所有项的方法通常很有用。这可以非常直观地用一个布尔值表示,该值表示是否选择了所有项。当设置为true
,它将选择所有项目,当设置为false
,它将取消选择他们。
Source code: View
<div class="heading">
<input type="checkbox" data-bind="checked: selectedAllProduce" title="Select all/none"/> Produce
</div>
<div data-bind="foreach: produce">
<label>
<input type="checkbox" data-bind="checkedValue: $data, checked: $parent.selectedProduce"/>
<span data-bind="text: $data"></span>
</label>
</div>
Source code: View model
function MyViewModel() {
this.produce = [ 'Apple', 'Banana', 'Celery', 'Corn', 'Orange', 'Spinach' ];
this.selectedProduce = ko.observableArray([ 'Corn', 'Orange' ]);
this.selectedAllProduce = ko.pureComputed({
read: function () {
// Comparing length is quick and is accurate if only items from the
// main array are added to the selected array.
return this.selectedProduce().length === this.produce.length;
},
write: function (value) {
this.selectedProduce(value ? this.produce.slice(0) : []);
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
例3:Value转换器
有时候你可能需要显示一些不同格式的数据,从基础的数据转化成显示格式。比如,你存储价格为float类型,但是允许用户编辑的字段需要支持货币单位和小数点。你可以用可写的依赖监控属性来实现,然后解析传入的数据到基本 float类型里:
Source code: View
<div>Enter bid price: <input data-bind="value: formattedPrice"/></div>
<div>(Raw value: <span data-bind="text: price"></span>)</div>
Source code: View model
function MyViewModel() {
this.price = ko.observable(25.99);
this.formattedPrice = ko.pureComputed({
read: function () {
return '$' + this.price().toFixed(2);
},
write: function (value) {
// Strip out unwanted characters, parse as float, then write the
// raw data back to the underlying "price" observable
value = parseFloat(value.replace(/[^\.\d]/g, ""));
this.price(isNaN(value) ? 0 : value); // Write to underlying storage
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
现在,每当用户输入一个新价格时,文本框都会更新,以显示它的格式是货币符号和两位小数,无论输入的格式是什么。这给了一个很好的用户体验,因为用户看到了软件如何将他们的数据输入理解为一个价格。他们知道他们不能输入超过两位小数,因为如果他们尝试输入,额外的小数位就会被删除。类似地,它们不能输入负值,因为write回调去掉了任何负号。
例4:过滤并验证用户输入
例1展示的是写操作过滤的功能,如果你写的值不符合条件的话将不会被写入,忽略所有不包括空格的值。
再多走一步,你可以声明一个监控属性 isValid
来表示最后一次写入是否合法,然后根据真假值显示相应的提示信息。稍后仔细介绍,先参考如下代码:
Source code: View
<div>Enter a numeric value: <input data-bind="textInput: attemptedValue"/></div>
<div class="error" data-bind="visible: !lastInputWasValid()">That's not a number!</div>
<div>(Accepted value: <span data-bind="text: acceptedNumericValue"></span>)</div>
Source code: View model
function MyViewModel() {
this.acceptedNumericValue = ko.observable(123);
this.lastInputWasValid = ko.observable(true);
this.attemptedValue = ko.pureComputed({
read: this.acceptedNumericValue,
write: function (value) {
if (isNaN(value))
this.lastInputWasValid(false);
else {
this.lastInputWasValid(true);
this.acceptedNumericValue(value); // Write to underlying storage
}
},
owner: this
});
}
ko.applyBindings(new MyViewModel());
现在,acceptedNumericValue
将只接受数字,其它任何输入的值都会触发显示验证信息,而会更新acceptedNumericValue
。
备注:上面的例子显得杀伤力太强了,更简单的方式是在<input>
上使用jQuery Validation
和number class
。Knockout可以和jQuery Validation一起很好的使用,参考例子:grid editor 。当然,上面的例子依然展示了一个如何使用自定义逻辑进行过滤和验证数据,如果验证很复杂而jQuery Validation很难使用的话,你就可以用它。
3 依赖跟踪如何工作的
新手没必要知道太清楚,但是高级开发人员可以需要知道为什么依赖监控属性能够自动跟踪并且自动更新UI…
事实上,非常简单,甚至说可爱。跟踪的逻辑是这样的:
- 当你声明一个依赖监控属性的时候,KO会立即调用执行函数并且获取初始化值
- 当你的执行函数运行的时候,KO会把所有需要依赖的依赖属性(或者监控依赖属性)都记录到一个Log列表里。
- 执行函数结束以后,KO会向所有Log里需要依赖到的对象进行订阅。订阅的callback函数是重新运行你的执行函数。然后回头重新执行上面的第一步操作(并且注销不再使用的订阅)。
- 最后KO会通知上游所有订阅它的订阅者,告诉它们我已经设置了新值。
所以,KO不仅仅是在第一次执行函数执行时候探测你的依赖项,每次它都会探测。举例来说,你的依赖属性可以是动态的:依赖属性A代表你是否依赖于依赖属性B或者C,这时候只有当A或者你当前的选择B或者C改变的时候执行函数才重新执行。你不需要再声明其它的依赖:运行时会自动探测到的。
另外一个技巧是:一个模板输出的绑定是依赖监控属性的简单实现,如果模板读取一个监控属性的值,那模板绑定就会自动变成依赖监控属性依赖于那个监控属性,监控属性一旦改变,模板绑定的依赖监控属性就会自动执行。嵌套的模板也是自动的:如果模板X render模板 Y,并且Y需要显示监控属性Z的值,当Z改变的时候,由于只有Y依赖它,所以只有Y这部分进行了重新绘制(render)。
4 PureComputed
Pure computed observables相对于一般的computed observables,在性能和存储上有优势,这是因为pure computed observables在不存在订阅者的时候是不会保持订阅关系的。这也使得pure computed observables有如下两点特点:
- 可以防止没有被订阅的computed observables的存储泄露。
- 可以降低因重复计算未被订阅的computed observables而造成的运算过载。
一个pure computed observable能够依据它是否拥有订阅者而自动地在两种状态下切换:
1.当不存在订阅者的时候,pure computed observable会进入休眠状态,此时的它,会关闭所有依赖于它的订阅关系,同时也不会再追踪它所关联的observables。一旦处于休眠状态的computed observable的值被读取的话,它就需要重新计算以便以确保值得正确性。
2.当它拥有订阅者的时候,pure computed observable会进入监听状态。一旦进入监听状态,它会立即调用它的function和订阅程序来追踪它所关联的observables。在这种状态下,pure computed observables和普通的computed observables无异。更为详细的内容需参考How dependency tracking works部分。
按照文档的说明,选择pure computed observables有两条原则。一是computed observable在运算的时候不能产生副作用(不能对其他的observables产生影响);二是computed observable的值应该仅仅依赖于它所关联的observables的值,而不是其他隐含的信息。
Pure computed observables有两种定义方式:
this.fullName = ko.pureComputed(function() {
return this.firstName() + " " + this.lastName();
}, this);
或者
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this, { pure: true });
4.1 参考:
Computed Observable 参考
下面的文档描述了如何构造和使用 Computed Observable。
构建一个 Computed Observable
ko.computed( evaluator [, targetObject, options] )
— 此表单支持创建 computed observable 的最常见情况。- evaluator — 用于computed observable 值的当前值的函数
- targetObject — 如果给定,则在KO调用回调函数时定义this的值。有关更多信息,请参见管理this功能的部分。
- options — An object with further properties for the computed observable. See the full list below.
ko.computed( options )
— This single parameter form for creating a computed observable accepts a JavaScript object with any of the following properties.
(一)Knockout 计算属性的更多相关文章
- Knockout开发中文API系列3–使用计算属性
计算属性 如果你已经有了一个监控属性 firstName和lastName,如果你想显示全名该怎么做呢?这个时候你就可以通过计算属性来实现,这个方法依赖于一个或多个监控属性,如果任何依赖对象发生改变他 ...
- Knockout.js(三):计算属性(Computed Observable)
在Knockout2.0之前,计算属性被称之为依赖属性,在2.0版本中,ko.dependentObservable重命名为ko.computed,因为它在读.解释和类型上更简单.在实际使用中,ko. ...
- Knockout v3.4.0 中文版教程-8-计算监控-纯计算属性
4.纯计算属性 纯计算监控在KO 3.2.0中开始引入,比大多数应用程序使计算监控有更大的性能提升和内存优化.这是因为在自身没有订阅的时候不会保持订阅状态.特性如下 阻止内存泄露 - 避免在应用程序里 ...
- knockoutjs 静动态数据、行为绑定,计算属性及Sync同步更新 Value值更新事件控制
data-bind="text: firstName"中data-bind属性是Knockout 用来显示关联UI和viewmodel的桥梁, text 表示把绑定的文本赋值给DO ...
- Vue - 在v-repeat中使用计算属性
1.从后端获取JSON数据集合后,对单条数据应用计算属性,在Vue.js 0.12版本之前可以在v-repeat所在元素上使用v-component指令 在Vue.js 0.12版本之后使用自定义元素 ...
- Vue之计算属性
上一篇里演示了计算属性的优点,但是,computed和data里的属性还是有区别的,computed的一个弱点就在于依赖于data属性的更新,才能触发视图更新. 举个例子: 上个例子中谈到用v-for ...
- 关于vue.js的计算属性练习代码
参照官网联系如下: <!DOCTYPE html><html lang="en"><head> <meta charset="U ...
- Vue 过滤器与计算属性
过滤器 V1.x 版本 过滤器基础 过滤器是一个通过输入数据,能够及时对数据进行处理并返回一个数据结果的简单函数.Vue有很多很便利的过滤器,可以参考官方文档,http://cn.vuejs.org/ ...
- 第三节:Vue计算属性
计算属性就是当其依赖的属性的值发生变化的时候,这个属性的值就会自动更新. 例子: <!DOCTYPE html> <html> <head> <meta ch ...
随机推荐
- Python开发第一篇
Python 是什么? 首先他可能是比较好的一个编程开发语言!
- python之旅十【第十篇】paramiko模块
paramiko模块介绍 ssh的远程连接 基于用户名密码的连接 import paramiko # 创建SSH对象 ssh = paramiko.SSHClient() # 允许连接不在know_h ...
- HBase轻松入门之HBase架构图解析
2018-12-13 2018-12-20 本篇文章旨在针对初学者以我本人现阶段所掌握的知识就HBase的架构图中各模块作一个概念科普.不对文章内容的“绝对.完全正确性”负责. 1.开胃小菜 关于HB ...
- BZOJ4643 卡常大水题 【Tarjan】
题目分析: 给所有边按A排序,依次加入再按B递增排序,势能分析可以发现是O(n^4)的 代码: #include<bits/stdc++.h> using namespace std; ; ...
- 满汉全席[2-SAT]
题面 对不起我又写了一个板题qvq 和洛谷那道模板题没区别...两样菜至少做一样即可 不过注意define和函数的区别!!! #include <cmath> #include <c ...
- 【Spring】手写Spring MVC
Spring MVC原理 Spring的MVC框架主要由DispatcherServlet.处理器映射.处理器(控制器).视图解析器.视图组成. 完整的Spring MVC处理 流程如下: Sprin ...
- JMeter 的调式工具
任何的编程工具都会相应的调式工具,JMeter的调式 工具主要有五种: 1.查看结果树:含请求信息.响应信息等 2.HTTP 镜像服务器:HTTP Mirror Server用于查看请求信息 3.De ...
- Numpy系列(四)- 索引和切片
Python 中原生的数组就支持使用方括号([])进行索引和切片操作,Numpy 自然不会放过这个强大的特性. 单个元素索引 1-D数组的单元素索引是人们期望的.它的工作原理与其他标准Python序 ...
- CMDB资产管理系统开发【day26】:linux客户端开发
客户端疑难点及获取流程 1.linux客户端支持2就可以,python3就是很麻烦 难道你要求所有的客户端都上pytho3吗? 现在从bin的入口进去 HouseStark.ArgvHandler(s ...
- 查看linux的cpu信息
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...