.example {
display: inline-block;
padding: 1em;
margin-right: 2em;
background: #F6F6EF;
}

注意:本文档适用于Knockout 3.4.0及更高版本。 对于先前版本,“延迟更新”插件提供类似的支持。

在复杂的应用程序中,由于具有多个交织的依赖关系,更新单个observable可能会触发计算的observable,手动订阅和UI绑定更新的级联。 如果将不必要的中间值推送到视图或产生额外的计算的可观察评估,则这些更新可能是昂贵的和低效的。 即使在简单的应用程序中,更新相关的可观察量或单个可观察的多次(例如填充可观察的数组)也可以具有类似的效果。

使用延迟更新可确保仅在计算的可观察量和绑定的依赖性稳定后才更新。 即使可观察者可能经历多个中间值,也只使用最新的值来更新其依赖性。 为了方便这一点,所有通知变为异步,使用Knockout微任务队列进行调度。 这听起来非常类似于速率限制,这也有助于防止额外的通知,但延迟更新可以在整个应用程序中提供这些好处,而不会增加延迟。 以下是标准,延迟和速率限制模式之间通知调度的不同之处:

  • Standard (标准)– 通知立即并同步。 通常通知中间值的依赖关系。
  • Deferred (延迟)– 通知异步发生,紧接在当前任务之后,通常在任何UI重绘之前发生。
  • Rate-limited (速率限制)– 通知发生在指定的时间段之后(根据浏览器的不同,最小值为2-10 ms)。

启用延迟更新

延迟更新默认关闭,以提供与现有应用程序的兼容性。 要为应用程序使用延迟更新,必须在通过设置以下选项初始化视图模型之前启用它:

ko.options.deferUpdates = true;

当deferUpdates选项打开时,所有observable,computed observable和绑定都将设置为使用延迟更新和通知。 在开始创建基于Knockout的应用程序时启用此功能意味着您不需要担心解决中间值问题,因此可以促进更清洁,纯粹的反应性设计。 但是,当启用现有应用程序的延迟更新时,应该注意,因为它会破坏依赖于同步更新或中间值通知的代码(尽管您可能可以解决这些问题)。

示例:避免多个UI更新

以下是一个有用的示例来演示延迟更新消除中间值的UI更新的能力,以及如何提高性能。

     

UI怨骂:

<!--ko foreach: $root-->
<div class="example">
<table>
<tbody data-bind='foreach: data'>
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: position"></td>
<td data-bind="text: location"></td>
</tr>
</tbody>
</table>
<button data-bind="click: flipData, text: 'Flip ' + type"></button>
<div class="time" data-bind="text: (data(), timing() + ' ms')"></div>
</div>
<!--/ko—>

视图模型源码:

function AppViewModel(type) {
this.type = type;
this.data = ko.observableArray([
{ name: 'Alfred', position: 'Butler', location: 'London' },
{ name: 'Bruce', position: 'Chairman', location: 'New York' }
]);
this.flipData = function () {
this.starttime = new Date().getTime();
var data = this.data();
for (var i = 0; i < 999; i++) {
this.data([]);
this.data(data.reverse());
}
}
this.timing = function () {
return this.starttime ? new Date().getTime() - this.starttime : 0;
};
} ko.options.deferUpdates = true;
var vmDeferred = new AppViewModel('deferred'); ko.options.deferUpdates = false;
var vmStandard = new AppViewModel('standard'); ko.applyBindings([vmStandard, vmDeferred]);

对特定observable使用延迟更新

即使您不为整个应用程序启用延迟更新,您仍然可以通过专门使某些可观察项延迟,从中受益。 使用延迟扩展器:

this.data = ko.observableArray().extend({ deferred: true });

现在我们可以将一堆项目推入数据数组,而不必担心引起过多的UI或计算更新。 延迟扩展器可以应用于任何类型的可观察量,包括可观察数组和计算可观察量。

示例:避免多个Ajax请求

以下模型表示可以作为分页网格呈现的数据:

function GridViewModel() {
this.pageSize = ko.observable(20);
this.pageIndex = ko.observable(1);
this.currentPageData = ko.observableArray(); // Query /Some/Json/Service whenever pageIndex or pageSize changes,
// and use the results to update currentPageData
ko.computed(function() {
var params = { page: this.pageIndex(), size: this.pageSize() };
$.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this);
}

因为计算的observable计算了pageIndex和pageSize,它变得依赖于它们。 所以,这个代码将使用jQuery的$ .getJSON函数重新加载currentPageData当一个GridViewModel首次实例化时,每当pageIndex或pageSize属性后来改变。

这是非常简单和优雅(和添加更多可观察的查询参数,也触发刷新自动每当他们改变)的,但有一个潜在的效率问题。 假设您向GridViewModel添加了以下函数,该函数同时更改pageIndex和pageSize:

this.setPageSize = function(newPageSize) {
// Whenever you change the page size, we always reset the page index to 1
this.pageSize(newPageSize);
this.pageIndex(1);
}

问题是,这将导致两个Ajax请求:第一个将在您更新pageSize时启动,第二个将在您更新pageIndex时立即启动。 这是浪费带宽和服务器资源,以及不可预测的竞争条件的来源。

当应用于计算的可观察量时,延迟的延长器也将避免对计算函数的过度评价。 使用延迟更新可确保对当前任务中的依赖性的任何更改顺序只触发对计算的observable的一次重新评估。 例如:

ko.computed(function() {
// This evaluation logic is exactly the same as before
var params = { page: this.pageIndex(), size: this.pageSize() };
$.getJSON('/Some/Json/Service', params, this.currentPageData);
}, this).extend({ deferred: true });

现在,您可以根据需要更改pageIndex和pageSize,并且Ajax调用只会在您将线程释放回JavaScript运行时后发生一次。

强制延迟通知

虽然延迟,异步通知通常更好,因为更少的UI更新,如果你需要立即更新UI可能是一个问题。 有时,为了正确的功能,您需要一个推送到UI的中间值。 您可以使用ko.tasks.runEarly方法完成此操作。 例如:

// remove an item from an array
var items = myArray.splice(sourceIndex, 1); // force updates so the UI will see a delete/add rather than a move
ko.tasks.runEarly(); // add the item in a new location
myArray.splice(targetIndex, 0, items[0]);

强制延迟可观察者总是通知订阅者

当any observable的值是原始值(数字,字符串,布尔值或null)时,只有当observable的依赖项设置为实际上与之前不同的值时,才会通知它的依赖项。 因此,原始值延迟可观测量只有在当前任务结束时它们的值实际上不同时才通知。 换句话说,如果一个原始值的延迟的observable被改变为一个新的值,然后改回原来的值,则不会发生通知。

要确保始终通知订阅者更新,即使该值相同,也可以使用notify扩展器:

ko.options.deferUpdates = true;

myViewModel.fullName = ko.pureComputed(function() {
return myViewModel.firstName() + " " + myViewModel.lastName();
}).extend({ notify: 'always' });

KnockoutJS 3.X API 第七章 其他技术(3) 延迟更新的更多相关文章

  1. KnockoutJS 3.X API 第七章 其他技术(4) 速率限制

    注意:这个速率限制API是在Knockout 3.1.0中添加的. 通常,更改的observable立即通知其订户,以便依赖于observable的任何计算的observable或绑定都会同步更新. ...

  2. KnockoutJS 3.X API 第七章 其他技术(8) 异步错误处理

    注意:本文档适用于Knockout 3.4.0及更高版本. ko.onError Knockout包装内部异步调用,并在抛出原始错误之前查找可选的ko.onError回调以执行(如果遇到异常). 这使 ...

  3. KnockoutJS 3.X API 第七章 其他技术(7) 微任务

    注意:本文档适用于Knockout 3.4.0及更高版本. Knockout的微任务队列 Knockout的微任务队列支持调度任务尽可能快地运行,同时仍然是异步的,努力安排它们在发生I / O,回流或 ...

  4. KnockoutJS 3.X API 第七章 其他技术(6) 使用“fn”添加自定义函数

    有时,您可能会通过向Knockout的核心值类型添加新功能来寻找机会来简化您的代码. 您可以在以下任何类型中定义自定义函数: 因为继承,如果你附加一个函数到ko.subscribable,它将可用于所 ...

  5. KnockoutJS 3.X API 第七章 其他技术(5) 使用其他事件处理程序

    在大多数情况下,数据绑定属性提供了一种干净和简洁的方式来绑定到视图模型. 然而,事件处理是一个常常会导致详细数据绑定属性的领域,因为匿名函数通常是传递参数的推荐技术. 例如: <a href=& ...

  6. KnockoutJS 3.X API 第七章 其他技术(2) 使用扩展器来增加可观察量(监控属性)

    Knockout observables提供了支持读取/写入值并在值改变时通知订阅者所需的基本功能. 但在某些情况下,您可能希望向可观察者添加其他功能. 这可能包括通过在可观察者前面放置一个可写的计算 ...

  7. KnockoutJS 3.X API 第七章 其他技术(1) 加载和保存JSON数据

    Knockout允许您实现复杂的客户端交互性,但几乎所有Web应用程序还需要与服务器交换数据,或至少将本地存储的数据序列化. 最方便的交换或存储数据的方式是JSON格式 - 大多数Ajax应用程序今天 ...

  8. KnockoutJS 3.X API 第六章 组件(5) 高级应用组件加载器

    无论何时使用组件绑定或自定义元素注入组件,Knockout都将使用一个或多个组件装载器获取该组件的模板和视图模型. 组件加载器的任务是异步提供任何给定组件名称的模板/视图模型对. 本节目录 默认组件加 ...

  9. KnockoutJS 3.X API 第六章 组件(3) 组件绑定

    组件绑定将指定的组件注入到元素中,并且可选地将参数传递给它. 本节目录 一个例子 API 组件生命周期 备注1:仅限模板组件 备注2:使用没有容器元素的组件 备注3:将标记传递给组件 处置和内存管理 ...

随机推荐

  1. selenium:org.openqa.selenium.WebDriverException: f.QueryInterface is not a function

    今天用selenium2遇到问题 org.openqa.selenium.WebDriverException: f.QueryInterface is not a function 查了好久最后终于 ...

  2. Java 程序员必须掌握的 Linux 命令(转:导师Jencks)

    1.查找文件 find / -name filename.txt根据名称查找/目录下的filename.txt文件. find . -name "*.xml"递归查找所有的xml文 ...

  3. three.js加载obj模型

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...

  4. 2016-2017 ACM-ICPC Asia-Bangkok Regional Contest

    A. WSI Extreme 将人按洗澡时间从大到小排序,那么$ans=\sum_{i=1}^{n}a_i\times\lfloor\frac{i+W-1}{W}\rfloor$. 当$W$比较大时, ...

  5. DataTable转换为JSON数组

    最后的格式为:[{},{},...] StringBuilder DataTableToJSON(DataTable dt) { string columnName; StringBuilder bu ...

  6. 应用TortoiseGit为github账号添加SSH keys

    每次同步或者上传代码到githun上的代码库时,需要每次都输入用户名和密码,这时我们设置一下SSH key就可以省去这些麻烦了.若果使用TortoiseGit作为github本地管理工具,Tortoi ...

  7. webservice 小小例子

    Web Service的主要目标是跨平台的可互操作性.为了实现这一目标,Web Service 完全基于XML(可扩展标记语言).XSD(XML Schema)等独立于平台.独立于软件供应商的标准,是 ...

  8. 6个奇葩的(hello,world)C语言版(转)

    //下面的所有程序都可以在GCC下编译通过,只有最后一个需要动用C++的编译器用才能编译通过. //程序功能输出   Hello,world! 01.c #define _________ } #de ...

  9. canvas 学习

    <!DOCTYPE html><html lang="en"><head> <meta charset="utf-8" ...

  10. java的英文词频算法

    java实现的英文词频算法,通常是采用单词树来实现的.使用java实现词频统计,为了统计词汇出现频率,最简单的做法是再建立一个map,其中,key是单词,value代表次数.将文章从头读到尾,读到一个 ...