4.纯计算属性
纯计算监控在KO 3.2.0中开始引入,比大多数应用程序使计算监控有更大的性能提升和内存优化。这是因为在自身没有订阅的时候不会保持订阅状态。特性如下
- 阻止内存泄露 - 避免在应用程序里计算监控不再被引用但是依赖仍然存在。
- 减少计算开销 - 当值不再被监控不会重新计算计算监控的值。
纯计算监控会在两个状态之间自动切换,基于它是否改变了订阅者。
- 当订阅者没有发生改变,它会休眠。当进入休眠状态,它释放所有自身依赖的订阅。在这种状态下,它不会订阅访问求值函数里面的的监控对象(尽管它会继续跟踪它们)。在休眠的时候计算监控的值被读取,如果依赖对象有发生改变,它会自动重新求值。
- 当订阅者发生任何改变。它会苏醒并开始监听。处于监听状态,它会马上订阅所有依赖。在这种状态下,它就像计算监控一样工作。
什么是"纯"函数?
(该概念属于函数式编程)
我们参考了pure
函数的术语,因为这个特性仅可以应用与计算监控,求值程序的纯功能描述如下 :
- 对计算监控求值不会引起任何其他影响。
- 计算监控的值不应该受求值规模或其他隐藏信息导致变化。它的值应该仅仅受程序里面其他监控对象的值导致变化,对于纯函数的定义,都考虑了它的参数。
语法
定义一个纯计算监控的标准方法是使用ko.purecomputed
:
this.fullName = ko.pureComputed(function() {
return this.firstName() + " " + this.lastName();
}, this);
或者你可以使用ko.computed
加pure
选项:
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this, { pure: true });
1. 什么情况下使用纯计算监控
你可以在任何计算监控上使用pure
特性。你会看到大部分的好处,尽管当它被应用到应用程序设计时,会使用到持久视图模型和共享临时视图和视图模型。在持久视图模型下使用纯计算监控会提供更好的计算性能。在临时视图模型下使用纯计算监控会提供更好的内存管理。
在下面这个简单的导航界面的例子中,纯计算监控属性fullName
仅仅在最后一个步骤激活的时候会刷新视图内容。
视图
<div class="log" data-bind="text: computedLog"></div>
<!--ko if: step() == 0-->
<p>First name: <input data-bind="textInput: firstName" /></p>
<!--/ko-->
<!--ko if: step() == 1-->
<p>Last name: <input data-bind="textInput: lastName" /></p>
<!--/ko-->
<!--ko if: step() == 2-->
<div>Prefix: <select data-bind="value: prefix, options: ['Mr.', 'Ms.','Mrs.','Dr.']"></select></div>
<h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
<!--/ko-->
<p><button type="button" data-bind="click: next">Next</button></p>
视图模型
function AppData() {
this.firstName = ko.observable('John');
this.lastName = ko.observable('Burns');
this.prefix = ko.observable('Dr.');
this.computedLog = ko.observable('Log: ');
this.fullName = ko.pureComputed(function () {
var value = this.prefix() + " " + this.firstName() + " " + this.lastName();
//通常情况下,你应该避免在一个纯计算监控里面对一个监控对象赋值(避免副作用)。但是这个例子是为了说明其内部工作原理,输出日志是一种很好的说明方式
this.computedLog(this.computedLog.peek() + value + '; ');
return value;
}, this);
this.step = ko.observable(0);
this.next = function () {
this.step(this.step() === 2 ? 0 : this.step()+1);
};
};
ko.applyBindings(new AppData());
2. 什么情况下不使用纯计算监控
副作用
在依赖项发生改变的时候需要执行一个操作的时候不应该使用计算监控的pure
特性。如例子所示:
- 基于多个监控的一个计算监控执行一个回调函数
ko.computed(function () {
var cleanData = ko.toJS(this);
myDataClient.update(cleanData);
}, this);
- 在绑定的初始化函数,使用计算监控更新绑定元素值。
ko.computed({
read: function () {
element.title = ko.unwrap(valueAccessor());
},
disposeWhenNodeIsRemoved: element
});
你不应该使用纯计算监控的原因是如果求值函数很容易引起严重的副作用,副作用就是无论何时计算未激活订阅(也就是休眠状态),求值函数就不会执行。当依赖改变,求值函数就必须执行,请使用计算监控。
3. 确定一个属性是否上纯计算监控
在某些场景,如果你用纯计算监控处理问题,通过程序确定某个属性是否是纯计算监控是很有用的。KO提供了一个工具函数,ko.isPureComputed
能处理这个问题。比如,您可能希望从返回服务器的数据中排除非纯计算监控值。
var result = {};
ko.utils.objectForEach(myObject, function (name, value) {
if (!ko.isComputed(value) || ko.isPureComputed(value)) {
result[name] = value;
}
});
4. 状态改变通知
当纯计算监控进入监听状态,它会通知awake
事件(使用其当前值),当它进入睡眠状态,它会通知一个asleep
事件(使用undefined值)。一般情况下,你不需要知道计算监控内部的状态。但是内部状态可以反应出计算监控是否绑定到了视图,你可能会利用那个状态信息做一些视图模型初始化或清理。
this.someComputedThatWillBeBound = ko.pureComputed(function () {
...
}, this);
this.someComputedThatWillBeBound.subscribe(function () {
// do something when this is bound
}, this, "awake");
this.someComputedThatWillBeBound.subscribe(function () {
// do something when this is un-bound
}, this, "asleep");
awake
事件在一般的计算监控用deferEvaluation
选项创建的时候也会响应。