注意:使用的Ext版本为4.2.1
本文指出一些书本和很多网站给出的、可能会让您的MVC程序隐藏有Bug的代码。如果你使用搜索引擎搜到这篇文章,那么你肯定已经使用了firebug或者opera dragonfly调试你的代码,发现了你的MVC程序中创建一个新的Controller的时候,该Controller的init方法执行了两次,而且很有可能由于init方法中初始化了对应的View,这个View就也被创建了两次,如果View中有一些定时执行的代码,那么就最糟了。本人的程序就是这样,两个同类型View,一个覆盖在了另外一个之上而被隐藏了,同时两个View还定时更新,一个View一旦更新了,它就会覆盖另外一个。
有关Extjs MVC应用程序编程的帖子或者书本(本人使用黄灯桥的《Ext js权威指南》也有误)中对于创建一个新的controller使用代码分别举例如下:
从一个博客copy的,使用ExtJS 4.1:
for (i = 0; i < ln; i++) {
controller = me.getController(controllers[i]);
controller.init(me);
}
黄灯桥的《Ext js权威指南》P952 switchPage函数中最后一行代码,使用的也是ExtJS 4.1:
me.getController(Ext.String.captitalize("your controller name")).init();
首先说明以上代码Ext4.1中应该是正确的,但是在Ext4.2版本中是错误的,而且这个错误还挺隐蔽的。原因就是4.2中,me.getController()方法源码已经执行了对应Controller的init方法,完了后上面的代码又调用了一次init()方法,看4.2中的Ext.app.Controller类的源代码:
/**
* Returns instance of a {@link Ext.app.Controller Controller} with the given id.
* When controller doesn't exist yet, it's created. Note that this method depends
* on Application instance and will return undefined when Application is not
* accessible. The only exception is when this Controller instance's id is requested;
* in that case we always return the instance even if Application is no available.
*
* @param {String} id
*
* @return {Ext.app.Controller} controller instance or undefined.
*/
getController: function(id) {
var me = this,
app = me.application;
if (id === me.id) {
return me;
}
return app && app.getController(id);
},
Controller类的getController其实调用了Ext.app.Application.getController()方法,源代码如下:
getController: function(name) {
var me = this,
controllers = me.controllers,
className, controller;
controller = controllers.get(name);
if (!controller) {
className = me.getModuleClassName(name, 'controller');
controller = Ext.create(className, {
application: me,
id: name
});
controllers.add(controller);
if (me._initialized) {
controller.doInit(me);
}
}
return controller;
},
if语句中,创建了controller并且执行了doInit方法,不再贴代码了,doInit执行了controller的init方法。
所以回头看代码:
me.getController(Ext.String.captitalize("your controller name")).init();
其实就是执行了两次controller的init方法,如果在controller的init方法中载入了View,那么就会产生两个View实例。
所以为了保险起见,正确的写法应该就是用getController方法就可以了,不要再加init():
me.getController(Ext.String.captitalize("your controller name"));
另外说明一下为什么在Extjs4.1中应该是正确的,上文中说到的博客和《Extjs权威指南》使用了Extjs4.1,其Ext.app.Controller.getController方法源码,比4.2来的简单:
/**
* Returns instance of a {@link Ext.app.Controller controller} with the given name.
* When controller doesn't exist yet, it's created.
* @param {String} name
* @return {Ext.app.Controller} a controller instance.
*/
getController: function(name) {
return this.application.getController(name);
},
和4.2一样,也是调用了Ext.app.Application.getConroller方法,上源码:
getController: function(name) {
var controller = this.controllers.get(name);
if (!controller) {
controller = Ext.create(this.getModuleClassName(name, 'controller'), {
application: this,
id: name
});
this.controllers.add(controller);
}
return controller;
},
可以对比上文中4.2的代码,发现4.1中没有
controller.doInit(me);
这行代码。