首页 > 代码库 > 让你的App飞一会
让你的App飞一会
在基于Backbone的单页应用中,我们可能会有下面这些疑问:
1 如果多次访问同一个页面(hash)时,被多次实例化的视图所占内存释放了么?
2 当你关闭视图后,是不是会发现,它上面的事件还没有移除掉?
3 你是不是在为进一个新页面之前的清理工作而懊恼呢?
因为Backbone是事件驱动的,在Backbone App 应用的许多地方你都能看到事件的身影:
在View中,通过events属性来配置事件的注册:
1 MyView = Backbone.View.extend({2 events: {3 "click #someButton": "doThat",4 "change #someInput": "changeIt"5 },6 doThat: function(){ ... },7 changeIt: function(){ ... }8 });
我们会监听model的变化来渲染页面:
1 MyView = Backbone.View.extend({2 initialize: function(){3 this.listenTo(this.model, this.render, this);4 },5 render: function(){ ... }6 });
我们还可以用全局/应用级的事件对象,来触发页面渲染:
1 MyView = Backbone.View.extend({2 initialize: function(){3 this.options.vent.bind("app:event", this.render, this);4 },5 render: function() { ... }6 });7 var vent = new _.extend({}, Backbone.Events);8 new MyView({vent: vent});9 vent.trigger("app:event");
事件在给我们带好处的同时,也有其负面影响。通过事件,多个对象会被绑在一起,它们之间也就有了引用关系。而这种引用关系如果不被清除,对象所占内存很可能不会被回收,引发内存泄露。
很常见的场景:
1 MyRouter = Backbone.Router.extend({ 2 routes: { 3 "": "home", 4 "post/:id": "showPost" 5 }, 6 7 home: function(){ 8 var homeView = new HomeView(); 9 $("#mainContent").html(homeView.render().el);10 },11 12 showPost: function(id){13 var post = posts.get(id);14 var postView = new PostView({model: post});15 $("#mainContent").html(postView.render().el);16 }17 });
如你所想,页面运行是没问题的。但你想过,当进入PostView视图时,HomeView视图上的事件有被清理过吗?它的实例会被回收吗?
我们来简单构造一个类,它专门负责视图上的清理工作:
1 function AppView(){ 2 3 this.showView(view) { 4 if (this.currentView){ 5 this.currentView.close(); 6 } 7 8 this.currentView = view; 9 this.currentView.render();10 11 $("#mainContent").html(this.currentView.el);12 }13 14 }
那之前的代码可优化如下:
1 MyRouter = Backbone.Router.extend({ 2 routes: { 3 "": "home", 4 "post/:id": "showPost" 5 }, 6 7 initialize: function(options){ 8 this.appView = options.appView; 9 },10 11 home: function(){12 var homeView = new HomeView();13 this.appView.showView(homeView);14 },15 16 showPost: function(id){17 var post = posts.get(id);18 var postView = new PostView({model: post});19 this.appView.showView(postView);20 }21 });
关闭视图
上面的例子中,当移除一个视图时,都会调用一个close方法,但Backbone.View是没有这个方法的,先讨论下,close方法要具备什么样的功能呢?
1 移除DOM元素上面的事件;
2 移除视图上面的事件;
3 从DOM树中移除相关HTML;
所以,我想close方法大概是这么个样子:
1 Backbone.View.prototype.close = function(){2 this.remove();3 this.off();4 }
首先我们这里调用Backbone.View的remove方法:
1 // Remove this view by taking the element out of the DOM, and removing any2 // applicable Backbone.Events listeners.3 remove: function () {4 this.$el.remove();5 this.stopListening();6 return this;7 }
“this.$el.remove()” 从DOM树中移除HTML,“this.stopListening()” 移除视图监听的其他对象上面的事件,” this.off()”移除视图自身的事件,但是看了zepto的remove方法:
1 remove: function(){2 return this.each(function(){3 if (this.parentNode != null)4 this.parentNode.removeChild(this)5 })6 },
对于视图里通过”events”来配置注册的事件是否被移除我持怀疑态度,这个暂不作讨论。
设计视图
1 Jass.extend = Backbone.View.extend; 2 Jass.View = Backbone.View.extend({ 3 initialize: function () { 4 var data=http://www.mamicode.com/arguments; 5 if(this.beforeRender) data=http://www.mamicode.com/this.beforeRender.apply(this,arguments)||{}; 6 this.render(this.tpl(data)); 7 if(this.afterRender) this.afterRender.call(this,data); 8 }, 9 close: function () {10 if(this.beforeClose) this.beforeClose();11 this.remove();12 this.off();13 if(this.onClosed) this.onClosed();14 //this.trigger(‘close‘);15 //delete this;16 },17 render: function (el) {18 this.$el.html(el);19 },20 });
在视图的渲染时,渲染前调用beforeRender,渲染后调用afterRender,可看成其生命周期的两个不同阶段,其中this.tpl= _.template(html模板)。
在close方法中,也有调用beforeClose和onClosed,做你想做的事情吧。
清道夫说,没有我,你们都是瞎忙活:
1 Jass.region = function (node) { 2 this.node = $(node); 3 } 4 Jass.region.prototype.show = function (view) { 5 var node = this.node; 6 var currentView = this.currentView; 7 if (!view.el) { 8 var err = new Error(‘View Must Extend from Jass.View‘); 9 err.name = ‘NoError‘;10 throw err;11 }12 if (view != currentView) {13 if (currentView) {14 currentView.close();15 delete this.currentView;16 }17 }18 node.html(view.el);19 if(view.onShow) view.onShow();20 view.trigger(‘render‘,view);21 this.currentView = view;22 return view;23 }
在框架里,先做些事情:
1 Jass.Application = function () {2 this.vent = _.extend({}, Backbone.Events);3 this._initCallbacks = new $.Callbacks();4 };5 6 App = _.extend(new Jass.Application,{7 "header": new Jass.region("#header"),8 "body": new Jass.region("#main"),9 });
然后在我们的App里会这样用:
1 var head=new headView();2 App.header.show(head);3 4 var body=new homeView();5 App.body.show(body);
打完收工,希望对你有所启示!
http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/
让你的App飞一会