Backbone.js:重新填充或重新创建视图?

sol*_*dil 82 javascript templates backbone.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()
    }
})
Run Code Online (Sandbox Code Playgroud)

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

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;
    }
})
Run Code Online (Sandbox Code Playgroud)

Joh*_*ika 136

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

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

我首先创建一个我的所有视图都继承自的BaseView.基本思想是我的View将保留对它所订阅的所有事件的引用,以便在处理View时,所有这些绑定将自动解除绑定.这是我的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;
Run Code Online (Sandbox Code Playgroud)

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

var SampleView = BaseView.extend({

    initialize: function(){
        this.bindTo(this.model, 'change', this.render);
        this.bindTo(this.collection, 'reset', this.doSomething);
    }
});
Run Code Online (Sandbox Code Playgroud)

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

var sampleView = new SampleView({model: some_model, collection: some_collection});
sampleView.dispose();
Run Code Online (Sandbox Code Playgroud)

我和正在编写"Backbone.js on Rails"电子书的人分享了这种技巧,我相信这是他们为本书采用的技术.

更新:2014-03-24

从Backone 0.9.9开始,listenTo和stopListening使用上面显示的相同bindTo和unbindFromAll技术添加到Events.此外,View.remove会自动调用stopListening,因此绑定和取消绑定就像现在一样简单:

var SampleView = BaseView.extend({

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

var sampleView = new SampleView({model: some_model});
sampleView.remove();
Run Code Online (Sandbox Code Playgroud)

  • 嗨SunnyRed,我更新了我的答案,以更好地反映我破坏观点的原因.使用Backbone,我认为没有理由在应用程序启动后重新加载页面,因此我的单页应用程序已经变得非常大.当用户与我的应用程序交互时,我会不断重新呈现页面的不同部分(例如,从详细信息切换到编辑视图),因此我发现始终创建新视图要容易得多,无论该部分先前是否已呈现或不.另一方面,模型代表业务对象,所以我只会在对象真正改变时修改它们. (2认同)

Der*_*ley 27

我最近在博客上写了这篇文章,并展示了我在我的应用程序中处理这些场景的几件事:

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


Bri*_*sio 8

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

detatch: function() {
   $(this.el).unbind();
   this.model.unbind();
Run Code Online (Sandbox Code Playgroud)

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

当然,正如您所提到的,您始终可以创建一个"详细信息"视图,而不会更改它.您可以绑定到模型上的"更改"事件(从视图中)以重新渲染自己.将其添加到初始化程序:

this.model.bind('change', this.render)
Run Code Online (Sandbox Code Playgroud)

这样做会导致细节窗格在每次对模型进行更改时重新呈现.您可以通过观察单个属性来获得更精细的粒度:"change:propName".

当然,这样做需要项目View引用的通用模型以及更高级别的列表视图和详细信息视图.

希望这可以帮助!


Ash*_*han 6

要多次修复事件绑定,

$("#my_app_container").unbind()
//Instantiate your views here
Run Code Online (Sandbox Code Playgroud)

在从路由实例化新视图之前使用上面的行,解决了我对僵尸视图的问题.

  • 在解开后我似乎无法重新渲染:\ (2认同)