我正在努力了解骨干,目前正在与僵尸观点进行斗争。我已经阅读了很多关于此事的堆栈溢出帖子,但我仍然无法弄明白。
为简单起见,我设置了两个需要切换的视图(没有数据)。 到目前为止我所做的是:
//define application object var app = { vent: {}, templates: {}, views: {}, routers: {}, }; //instantiate event aggregator and attach it to app app.vent = _.extend({}, Backbone.Events);
定义两个非常简单的模板(存储到app.templates中):第一个模板有一些虚拟文本和一个按钮(其中和id为' test-begin'),第二个只是虚拟文本
定义两个视图
app.views.instructions = Backbone.View.extend({ //load underscore template template: _.template(app.templates.instructions), //automatically called upon instantiation initialize: function(options) { //bind relevant fucntions to the view _.bindAll(this, 'render', 'testBegin', 'stillAlive', 'beforeClose'); //listen to app.vent event this.listenTo(app.vent, 'still:alive', this.stillAlive); }, //bind events to DOM elements events: { 'click #test-begin' : 'testBegin', }, //render view render: function() { this.$el.html(this.template()); return this; }, //begin test testBegin: function() { Backbone.history.navigate('begin', {trigger: true}); }, //still alive stillAlive: function() { console.log('I am still alive'); }, //before closing beforeClose: function() { //stop listening to app.vent this.stopListening(app.vent); }, }); //test view app.views.test = Backbone.View.extend({ //load underscore template template: _.template(app.templates.test), //automatically called upon instantiation initialize: function(options) { //trigger still:alive and see if removed view responds to it app.vent.trigger('still:alive'); //bind relevant fucntions to the view _.bindAll(this, 'render'); }, //render view render: function() { this.$el.html(this.template()); return this; }, });
//base router app.routers.baseRouter = Backbone.Router.extend({ //routes routes: { '': "instructions", 'begin': "beginTest" }, //functions (belong to object controller) instructions: function() {baseController.instructions()}, beginTest : function() {baseController.beginTest()}, }); //baseRouter controller var baseController = { instructions: function() { mainApp.viewsManager.rederView(new app.views.instructions()); }, beginTest: function(options) { mainApp.viewsManager.rederView(new app.views.test()); }, };
//define mainApplication object mainApp = {}; //manages views switching mainApp.viewsManager = { //rootEl rootEl: '#test-container', //close current view and show next one rederView : function(view, rootEl) { //if DOM el isn't passed, set it to the default RootEl rootEl = rootEl || this.rootEl; //close current view if (this.currentView) this.currentView.close(); //store reference to next view this.currentView = view; //render next view $(rootEl).html(this.currentView.render().el); }, }; //render first view of app mainApp.viewsManager.rederView(new app.views.instructions()); //initiate router and attach it to app mainApp.baseRouter = new app.routers.baseRouter(); //start Backbone history Backbone.history.start({silent: true });
//add function to Backbone view prototype (available in all views) Backbone.View.prototype.close = function () { //call view beforeClose function if it is defined in the view if (this.beforeClose) this.beforeClose(); //this.el is removed from the DOM & DOM element's events are cleaned up this.remove(); //unbind any model and collection events that the view is bound to this.stopListening(); //check whether view has subviews if (this.hasOwnProperty('_subViews')) { //loop thorugh current view's subviews _(this._subViews).each(function(child){ //invoke subview's close method child.close(); }); } };
因此,为了检查僵尸视图,第二个视图触发和事件(仍然是:alive)第一个视图通过发送到console.log的消息来监听并响应它(尽管它真的不应该&#39 ; T)。 第一个视图确实收听了这样的消息(在控制台日志中我读到了#39;我还活着),即使它被第二个视图替换了。
你能帮帮我吗?非常感谢你。答案 0 :(得分:6)
长篇大论,如果您有任何疑问,请询问
僵尸视图只是一个不在DOM中的视图,但是监听事件并对事件作出反应 - 有时这种行为是预期的,但通常不是。
如果未正确删除视图的DOM事件处理程序,则不会对视图及其内存中的HTML片段进行垃圾回收。如果Backbone.Event处理程序未正确绑定,您可能会遇到各种不良行为......例如一堆“Zombie”视图在模型上触发AJAX请求。在stopListening
和listenTo
之前的旧版Backbone上,此问题非常常见,尤其是如果您在视图之间共享模型。
在您的代码中,您没有Zombie View,因为您正在关闭视图。
您可以看到console.log
因为您在关闭第一个视图之前初始化第二个视图(并触发事件still:alive
)。
要切换视图,请致电:
mainApp.viewsManager.rederView(new app.views.test());
调用new app.views.test()
初始化第二个视图,该视图触发第一个侦听的事件。
如果您将代码更新为以下内容,则不会再看到console.log
。
//baseRouter controller
var baseController = {
instructions: function() {
mainApp.viewsManager.rederView(app.views.instructions);
},
beginTest: function(options) {
mainApp.viewsManager.rederView(app.views.test);
},
};
并更新rederView
rederView : function(ViewClass, rootEl) {
//if DOM el isn't passed, set it to the default RootEl
rootEl = rootEl || this.rootEl;
//close current view
if (this.currentView) this.currentView.close();
//store reference to next view
this.currentView = new ViewClass();
//render next view
$(rootEl).html(this.currentView.render().el);
},
如果从close方法中删除此行,您将拥有一个僵尸视图,并且应该看到console.log
。
//unbind any model and collection events that the view is bound to
this.stopListening();
在下面的代码中,我创建了100个视图,但只在DOM中显示1。每个视图都包含相同的模型并监听它的change
事件。单击视图的<button>
元素时,它会更新模型,导致每个视图的模型更改处理程序被执行,调用fetch 100次... 100个AJAX请求!
视图的更改处理程序被调用100次,因为视图关闭方法不会调用this.stopListening()
,因此即使从页面中删除视图,它们仍然会监听模型的事件。单击该按钮后,模型将更改,并且所有僵尸视图都会响应,即使它们不在页面上。
var TestView = Backbone.View.extend({
tagName: 'h1',
initialize: function(options) {
this.i = options.i;
this.listenTo(options.model, 'change', function(model) {
model.fetch();
});
},
events: {
'click button': function() {
this.model.set("show_zombies", Date.now());
}
},
render: function() {
this.$el.append("<button>Click To Test for Zombies!</button>");
return this;
},
close: function() {
this.$el.empty(); // empty view html
// this.$el.off(); // // Whoops! Forgot to unbind Event listeners! (this view won't get garbage collected)
// this.stopListening() // Whoops! Forgot to unbind Backbone.Event listeners.
}
});
var model = new (Backbone.Model.extend({
fetch: function() {
document.body.innerHTML += "MODEL.FETCH CALLED<br />"
}
}));
var v;
for (var i = 1; i < 101; i++) {
if (v) v.close();
v = new TestView({
'i': i,
'model': model
}).render();
$('body').html(v.el);
}
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone.js"></script>