深入ViewModel-ViewModel Internals
我们已经了解了 ViewModel 能做什么,我们再来深入了解一下 ViewModel 内部的机制,有助于问题的跟踪和诊断。
ViewModel 主要负责两个工作:管理对数据对象的变更,当数据变更时安排计划绑定。
ViewModel Data and Inheritance
ViewModel 类管理一个“数据”对象,利用 JavaScript 的原型链(prototype chain)实现继承。如下图所示:
这说明,所有的组件都可以读取上层容器(top-level container)设置的属性(存储在上层容器的数据对象,Data 1
)。假设 Container 1
的 ViewModel 如下:
viewModel: {
data: {
foo: 42
}
}
这允许所有的组件绑定到 {foo}。这种方式常用于跟踪应用程序的任何层次都需要访问的重要记录(例如:当前登录用户)。事实上,由于使用 JavaScript 的原型链提供数据,当数据变更后,如果需要共享变更的属性,可以在 ViewModel 中发布(publish)一个对象。在子容器 Container 2
中考虑使用一个双向绑定到 {foo}:
{
xtype: 'textfield',
bind: '{foo}'
}
这个文本框通过 Data 2
的原型链从 Data 1
获取到 foo
属性的值 42
。如果该组件修改了内容,这个值是存储在 Data 2
上的。这是因为组件是绑定到 ViewModel 上的,双向绑定在 ViewModel 2
上生效,像普通的 JavaScript 对象一样,设置 Data 2
的 foo
属性。。这种模式也可以用于初始化一些值,然后从视图中分离出来。
为了要实时共享继承的属性,应该使用存储在根 ViewModel 上的对象:
viewModel: {
data: {
stuff: {
foo: 42
}
}
}
现在,双向绑定将更新共享的 stuff
对象的 foo
属性:
{
xtype: 'textfield',
bind: '{stuff.foo}'
}
计划和依赖-Scheduling and Dependencies
加快数据绑定处理的关键是避免冗余和不必要的计算。
为了管理这些事项, ViewModel 跟踪数据间的依赖关系。每个绑定和公式都有一个依赖。ViewModel 将这些依赖关系分解为间接的关系,同时建立一个线性的计划,当数据变化时,这个计划将被延期进行处理。
所以,当你设置或修改 ViewModel 数据的值或数据记录的属性时,不必担心立即发生大量的重新计算。同样,如果有 7 个公式,每个公式使用另一个公式(所以有 7 层的深度链),每个公式又依赖其他 7 个值,变更这 49 个值,每个公式只会被重新计算一次。
为了达到这个目的,这些依赖项必须能够被 ViewModel 所识别,并且必须是非循环引用的。循环引用将引发错误。例如:
Ext.define('App.view.broken.BrokenModel', {
extend: 'Ext.app.ViewModel',
formulas: {
bar: function (get) {
return get('foo') / 2;
},
foo: function (get) {
return get('bar') * 2;
}
}
});
在实际的应用程序中,这样的 bug 可能不是特别明显,但是很显然, foo
和 bar
相互依赖,无法确定计算顺序以得到一个正确的结果。
公式依赖-Formula Dependencies
当一个公式使用一个明确的绑定,那么它的依赖关系是明显的。当一个公式只提供了一个函数或者一个 get
方法,那么 ViewModel 将分析函数的内容,搜索正确的引用关系。详细信息参见 Ext.app.bind.Formula
。