Js: 重新填充还是重新创建视图?

在我的 Web 应用程序中,左边的表中有一个用户列表,右边有一个用户详细信息窗格。当管理员单击表中的用户时,其详细信息应该显示在右侧。

左边是 UserListView 和 UserRowView,右边是 UserDetailView。事情还算顺利,但我的行为很奇怪。如果我单击左侧的某些用户,然后单击其中一个用户上的删除,我将获得显示的所有用户的连续 javascript 确认框。

看起来所有先前显示的视图的事件绑定都没有被删除,这似乎是正常的。我不应该在 UserRowView 上每次都做一个新的 UserDetailView?我是否应该维护一个视图并更改其参考模型?在创建新视图之前,是否应该跟踪当前视图并删除它?我有点迷路了,任何想法都欢迎。谢谢!

下面是左视图的代码(行显示、单击事件、右视图创建)

window.UserRowView = Backbone.View.extend({
tagName : "tr",
events : {
"click" : "click",
},
render : function() {
$(this.el).html(ich.bbViewUserTr(this.model.toJSON()));
return this;
},
click : function() {
var view = new UserDetailView({model:this.model})
view.render()
}
})

和右视图的代码(删除按钮)

window.UserDetailView = Backbone.View.extend({
el : $("#bbBoxUserDetail"),
events : {
"click .delete" : "deleteUser"
},
initialize : function() {
this.model.bind('destroy', function(){this.el.hide()}, this);
},
render : function() {
this.el.html(ich.bbViewUserDetail(this.model.toJSON()));
this.el.show();
},
deleteUser : function() {
if (confirm("Really delete user " + this.model.get("login") + "?"))
this.model.destroy();
return false;
}
})
33224 次浏览

这是很常见的情况。如果每次都创建一个新视图,则所有旧视图仍将绑定到所有事件。您可以在视图上创建一个名为 detatch的函数:

detatch: function() {
$(this.el).unbind();
this.model.unbind();

然后,在创建新视图之前,确保对旧视图调用 detatch

当然,正如您所提到的,您总是可以创建一个“详细”视图,并且永远不要更改它。您可以绑定到模型上的“ change”事件(从视图)来重新呈现您自己。将这个添加到初始化器:

this.model.bind('change', this.render)

这样做将导致每次对模型进行更改时重新呈现详细信息窗格。通过查看一个属性: “ change: proName”,可以获得更细的粒度。

当然,这样做需要一个公共模型,项目视图引用这个模型,还需要更高级别的列表视图和详细信息视图。

希望这个能帮上忙!

我最近在博客上写到了这一点,并展示了我在应用程序中处理这些场景所做的一些事情:

Http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/

我总是破坏和创建视图,因为随着我的单页应用程序变得越来越大,将未使用的实时视图保存在内存中,以便我可以重用它们将变得难以维护。

下面是我用来清理视图以避免内存泄漏的技术的简化版本。

我首先创建一个 BaseView,我的所有视图都从它继承。其基本思想是,我的视图将保留对它所订阅的所有事件的引用,这样当到了释放视图的时候,所有这些绑定都将自动解除绑定。下面是我的 BaseView 的一个实现示例:

var BaseView = function (options) {


this.bindings = [];
Backbone.View.apply(this, [options]);
};


_.extend(BaseView.prototype, Backbone.View.prototype, {


bindTo: function (model, ev, callback) {


model.bind(ev, callback, this);
this.bindings.push({ model: model, ev: ev, callback: callback });
},


unbindFromAll: function () {
_.each(this.bindings, function (binding) {
binding.model.unbind(binding.ev, binding.callback);
});
this.bindings = [];
},


dispose: function () {
this.unbindFromAll(); // Will unbind all events this view has bound to
this.unbind();        // This will unbind all listeners to events from
// this view. This is probably not necessary
// because this view will be garbage collected.
this.remove(); // Uses the default Backbone.View.remove() method which
// removes this.el from the DOM and removes DOM events.
}


});


BaseView.extend = Backbone.View.extend;

每当视图需要绑定到模型或集合上的事件时,我都会使用 bindTo 方法。例如:

var SampleView = BaseView.extend({


initialize: function(){
this.bindTo(this.model, 'change', this.render);
this.bindTo(this.collection, 'reset', this.doSomething);
}
});

每当我删除一个视图时,我只需要调用 pose 方法,它会自动清除所有内容:

var sampleView = new SampleView({model: some_model, collection: some_collection});
sampleView.dispose();

我和那些正在写“ Backbone.js on Rails”电子书的人们分享了这个技术,我相信这就是他们为这本书所采用的技术。

更新: 2014-03-24

从 Backone 0.9.9开始,listenTo 和 stoplisten 被添加到 Events 中,使用的 bindTo 和 unbindFromAll 技术与上面显示的相同。另外,View.remove 调用 stop 自动监听,所以绑定和解除绑定现在就像这样简单:

var SampleView = BaseView.extend({


initialize: function(){
this.listenTo(this.model, 'change', this.render);
}
});


var sampleView = new SampleView({model: some_model});
sampleView.remove();

我认为大多数人从 Backbone 开始会创建代码中的视图:

var view = new UserDetailView({model:this.model});

这段代码创建了僵尸视图,因为我们可能会不断创建新视图而不清理现有视图。但是对应用程序中的所有主干视图调用 view.pose ()不方便(特别是在 for 循环中创建视图时)

我认为在创建新视图之前放置清理代码的最佳时机。我的解决方案是创建一个助手来进行清理:

window.VM = window.VM || {};
VM.views = VM.views || {};
VM.createView = function(name, callback) {
if (typeof VM.views[name] !== 'undefined') {
// Cleanup view
// Remove all of the view's delegated events
VM.views[name].undelegateEvents();
// Remove view from the DOM
VM.views[name].remove();
// Removes all callbacks on view
VM.views[name].off();


if (typeof VM.views[name].close === 'function') {
VM.views[name].close();
}
}
VM.views[name] = callback();
return VM.views[name];
}


VM.reuseView = function(name, callback) {
if (typeof VM.views[name] !== 'undefined') {
return VM.views[name];
}


VM.views[name] = callback();
return VM.views[name];
}

使用 VM 创建视图将有助于清理任何现有的视图,而不必调用 view.pose ()。您可以对代码进行一个小的修改

var view = new UserDetailView({model:this.model});

var view = VM.createView("unique_view_name", function() {
return new UserDetailView({model:this.model});
});

因此,如果您想要重用视图而不是不断地创建视图,这取决于您,只要视图是干净的,您就不需要担心。只需将 createView 更改为 reuseView:

var view = VM.reuseView("unique_view_name", function() {
return new UserDetailView({model:this.model});
});

详细的代码和属性张贴在 https://github.com/thomasdao/Backbone-View-Manager

一种替代方法是绑定,而不是创建一系列新视图然后解除这些视图的绑定。你可以这样做:

window.User = Backbone.Model.extend({
});


window.MyViewModel = Backbone.Model.extend({
});


window.myView = Backbone.View.extend({
initialize: function(){
this.model.on('change', this.alert, this);
},
alert: function(){
alert("changed");
}
});

您将 myView 的模型设置为 myViewModel,而后者将被设置为 User 模型。这样,如果您将 myViewModel 设置为另一个用户(即,更改其属性) ,那么它可以在视图中触发一个具有新属性的呈现函数。

一个问题是,这打破了与原始模型的链接。您可以通过使用集合对象,或者通过将用户模型设置为 viewmodel 的属性来绕过这个问题。然后,这将在视图中作为 myview.model. get (“ model”)访问。

要修复多次绑定的事件,

$("#my_app_container").unbind()
//Instantiate your views here

在从路由实例化新的视图之前使用上面的代码行,解决了我遇到的僵尸视图的问题。

使用此方法清除内存中的子视图和当前视图。

//FIRST EXTEND THE BACKBONE VIEW....
//Extending the backbone view...
Backbone.View.prototype.destroy_view = function()
{
//for doing something before closing.....
if (this.beforeClose) {
this.beforeClose();
}
//For destroying the related child views...
if (this.destroyChild)
{
this.destroyChild();
}
this.undelegateEvents();
$(this.el).removeData().unbind();
//Remove view from DOM
this.remove();
Backbone.View.prototype.remove.call(this);
}






//Function for destroying the child views...
Backbone.View.prototype.destroyChild  = function(){
console.info("Closing the child views...");
//Remember to push the child views of a parent view using this.childViews
if(this.childViews){
var len = this.childViews.length;
for(var i=0; i<len; i++){
this.childViews[i].destroy_view();
}
}//End of if statement
} //End of destroyChild function




//Now extending the Router ..
var Test_Routers = Backbone.Router.extend({


//Always call this function before calling a route call function...
closePreviousViews: function() {
console.log("Closing the pervious in memory views...");
if (this.currentView)
this.currentView.destroy_view();
},


routes:{
"test"    :  "testRoute"
},


testRoute: function(){
//Always call this method before calling the route..
this.closePreviousViews();
.....
}




//Now calling the views...
$(document).ready(function(e) {
var Router = new Test_Routers();
Backbone.history.start({root: "/"});
});




//Now showing how to push child views in parent views and setting of current views...
var Test_View = Backbone.View.extend({
initialize:function(){
//Now setting the current view..
Router.currentView = this;
//If your views contains child views then first initialize...
this.childViews = [];
//Now push any child views you create in this parent view.
//It will automatically get deleted
//this.childViews.push(childView);
}
});