MVVM是模型(Model)、视图(View)和视图模型(ViewModel)的缩写,是一种软件设计模式。
MVVM主要作用就是简化事件驱动变成的用户界面开发。
它把软件系统划分为模型、视图和视图模型三个基本部分
2.1 Extjs中的MVVM模式
Extjs中的MVVM模式将应用程序划分为视图、视图控制器和视图模型三个部分,
模型:用来显示数据的各种UI组件,如模板(Ext.Template)、数据视图(Ext.view.View)、网格(Ext.grid.Panel)或图表(Ext.chart.Chart)等组件。
视图控制器:一般是指从Ext.app.ViewController扩展的类,用于维护视图的逻辑。它的生命周期是包含在视图的生命周期中的,会随视图的创建而创建,随视图的销毁而销毁。同一个视图,创建了多少个实例,也会相应的创建多个视图控制器的实例,而不会想ExtJs4那样,无论多少个视图实例,都只对一个控制器,从而造成控制器不知道该去操控哪个视图的问题,也解决了控制器销毁的问题。
模型:一般是指从Ext.data.Model扩展出来的模型类。
视图模型:一般是指从Ext.app.ViewModel扩展的类,用于管理视图的数据。
2.2 组件查询机制
ExtJs6中引入了医用机制来获取组件,但很多时候还是需要使用到组件查询来获取组件,因而熟练掌握并了解组件查询对象是必不可少的。
2.21 组件管理器:Ext.ComponentManager
组件能够实现查询全靠组件管理器,因为在创建组件实例时都需要在组件管理器中注册,在Ext.Component类的构造函数中可以看到以下代码
constructor:function(config){
var me = this,
......
if(!me.preventRegister){
Ext.ComponentManager.register(me);
}
.......
}
在ExtJs6中,Ext.Component是所有组件的根类,而组件在初始化的时候,都会在构造函数中调用callParent方法来执行父类的构造函数,直至根类的构造函数。也就是说,无论组件继承层次有多深,都会执行Ext.Component的构造函数。从上面所列的Ext.Component的构造函数的代码的粗体代码中可以看到,如果没有生命preventRegister为true,就会调用Ext.ComponentManager的register方法来注册组件实例。
register: function(){
var me = this;
all = me.all,
key = component.getId(),
onAvailableCallbacks = me.onAvalilableCallbacks;
//
if(key == undefined){
Ext.raise('Component id is undefined. Please ensure the component has an id.');
}
if(key in all){
Ext.raise('Registering duplicate component id "' + key + '"');
}
//
all[key] = component;
if(component.getReference && component.getReference()){
me.references[key] = component;
}
++me.count;
if(!me.hasFocusListener){
Ext.on('focus', me.onGlobalFocus, me);
me.hasFocusListener = true;
}
onAvailableCallbacks = onAvailableCallbacks && onAvailableCallbacks[key];
if(onAvailableCallbacks && onAvailableCallbacks.length){
me.notifyAvailable(component);
}
}
从粗体代码可以看到,register方法会通过组件的getId方法返回组建的标识符,并把该标识符作为all对象的属性名称,而该属性的属性值为组件。
每当创建组件实例的时候,都会在组件管理器中进行注册,也就是将组件实例保存到一个对象中,通过组件的标识符就可以轻松的找到这个组件。
2.2.2 组件的查询方式
1、使用id进行查询
Ext.ComponentQuery.query("#mypanel");
2、根据组件的别名进行查询(xtype)
Ext.ComponentQuery.quer("panel");
Ext.ComponentQuery.query(".panel");
3、使用属性查询
Ext.ComponentQuery.query("panel[title=我的面板]");
4、使用成员函数进行查询
Ext.ComponentQuery.query("{成员函数}");
例如要查询被禁用的按钮
Ext.ComponentQuery.query("{isDisabled()}");
5、伪类查询
组件查询还可以想DOM查询一样实现伪类查询,不过目前只定义了not这样一个伪类查询,目的是用来查询不匹配选择符的组件
2.2.3 直接使用id查询组件
Ext.getCmp = function(id){
return Ext.ComponentManager.get(id);
}
这里调用了Ext.ComponentManager的get方法,它的源代码如下:
get : function(id){
return this.all.get(id);
}
2.2.4 组件中的查询
组件中的查询依赖与组件树,往上可追溯父组件,往下可查找子组件。
组件中的查询主要包括 up、down、query、child、nextNode、nextSibiling、previousNode和previousSibling这8个方法。
1、Ext.Component的up方法
Ext.Component的up方法的作用是查找当前组件的父组件。
2、Ext.Container的down方法
down方法是在Ext.Container中定义的,用来查找子组件。
down方法的使用与up方法的区别不大,需要注意的是,down方法返回的是符合条件的第一个组件,而且只能在容器类 及其子类中使用。
3、Ext.Container的query方法
query方法返回的是由组件组成的数组。剩下的使用方法与up跟down方法没什么区别。
4、Ext.Container的child方法
返回容器下直接组件的第一个子组件。
5、Ext.Component的nextNode、nextSibiling、previousNode和previousSibling方法
这4个方法是用来查询当前组件的前一个节点或下一个节点的。nextNode(previousNode)与nextSibiling(previousSibiling)的主要区别是nextSibiling查询的是与当前节点同层的节点,而nextNode则没有这个限制。
2.3 视图控制器
视图控制器相对于ExtJs4的控制器来说,最大的区别就在于视图控制器采用的是声明式事件监听
比如:
{xtype : 'button', text : '保存', handler : 'onSave'}
定义了一个保存按钮,handler配置项应该是一个函数,以实现按钮的单机操作,而现在定义的是一个方法名称,这就是声明式事件监听。
那这个是怎么实现的呢?在sencha内部,把这一过程称为事件监听作用域解析,简单来说就是在组件或视图控制器内搜索对应的方法。搜索会先从组件所在的视图或视图控制器开始,如果没有找到,就会沿着组件树往上一层层的找,直到找到对应的方法。实在找不到就会抛出错误。
由于视图控制器与视图是一对一的关系,因此在视图控制器中引入以下4个方法来对应视图的关键点任务:
beforinit : 该方法会在视图的initComponent方法执行之前执行。该方法如果放在initComponent方法内,就相当与在调用callParent方法之前的代码,如调整子组件等操作。
init : 该方法会在initComponent方法执行之后执行,也就是在initComponent方法内调用callParent方法之后的代码,如调用on方法为子组件绑定事件等操作。
initViewModel : 该方法会在视图模型(如果定义了视图模型)创建之后执行。
destory : 清理和回收资源。如果视图需要多次创建和销毁,且在视图内创建了Ext.util.KeyNav之类的对象,就必须在该方法内销毁对象,以防止出现内存泄漏。在该方法内,千万别忘了调用callParent方法以执行父类的清理工作。
2.4 视图模型
视图模型的主要作用是用来管理用于绑定的数据对象。在视图模型中常用的配置项主要包含data、formulas、和stores。
data主要用来定义除了存储之外的任务数据对象,它的值为配置对象。在配置对象中,每一个属性为一个数据对象,如面板的标题希望通过绑定的方式来实现切换,就可在定义面板时添加以下代码来实现绑定:
bind : {title : '{title}'}
配置项bind表示这里的内容要实现绑定操作。属性title表示要绑定面板的标题属性,而要绑定的数据对象为title.
formulas主要作用是可以对data内的数据对象做一些运算后返回运算结果,如为网格绑定selecion属性以便获取选择行:
bind : {selection : '{selected}'}
希望在视图模型中获取选择行内某列的信息,如title列,就可以在formulas配置项内定义一个getTitile的方法来获取选择行的标题:
data : {
selected : null
}
formulas : {
getTitle : function(){
var selected = get('selected');
if(Ext.isEmpty(selected)) return ' ';
return selected.data.title;
}
}
store主要用来定义视图所需的存储
stores : {
users : {
...............
},
roles : {
...............
}
}
如果组件需要绑定存储,就可以通过类似如下代码的方式来绑定存储:
bind : {store : '{users}'}