Javascript 在 Backbone.js 中处理视图和模型对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7379263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 02:02:54  来源:igfitidea点击:

Disposing of view and model objects in Backbone.js

javascriptbackbone.js

提问by user802232

Which is the most efficient way to dispose model/view instances when not needed?

在不需要时处理模型/视图实例的最有效方法是什么?

Usually, I put all the logic in the controller/router. It is the one that decides, what views should be created, and what models should be supplied to them. Usually, there are a few handler functions, corresponding to different user actions or routes, where I create new view instances every time when a handler gets executed. Of course, that should eliminate whatever I've previously stored in the view instance. However, there are some situations when some views keep DOM event handlers themselves, and they don't get unbinded properly, which results in those instances being kept alive. I wish if there were a proper way to destroy view instances, when for example their el (DOM representation) gets detached, or thrown out of the DOM

通常,我将所有逻辑放在控制器/路由器中。它是决定应该创建什么视图以及应该向它们提供什么模型的人。通常,有一些处理程序函数,对应于不同的用户操作或路由,每次执行处理程序时,我都会在其中创建新的视图实例。当然,这应该消除我之前存储在视图实例中的任何内容。但是,在某些情况下,某些视图本身保留了 DOM 事件处理程序,并且它们没有正确解除绑定,这会导致这些实例保持活动状态。我希望是否有一种适当的方法来销毁视图实例,例如它们的 el(DOM 表示)被分离,或者被抛出 DOM

回答by Derick Bailey

you're on the right path. you should have an object that controls the lifecycle of your views. i don't like to put this in my view. i like to create a separate object for this.

你走在正确的道路上。您应该有一个控制视图生命周期的对象。我不喜欢把这个放在我的观点中。我喜欢为此创建一个单独的对象。

the thing you need to do, is unbind the events when necessary. to do this, it's a good idea to create a "close" method on all of your views, and use the object that controls the lifecycle of everything to always call the close method.

您需要做的事情是在必要时取消绑定事件。为此,最好在所有视图上创建一个“close”方法,并使用控制一切生命周期的对象始终调用 close 方法。

for example:

例如:


  function AppController(){
    this.showView = function (view){
      if (this.currentView){
        this.currentView.close();
      }
      this.currentView = view;
      this.currentView.render();
      $("#someElement").html(this.currentView.el);
    }
  }

at this point, you would set up your code to only have one instance of the AppController, and you would always call appController.showView(...)from your router or any other code that needs to show a view in the #someElementportion of your screen.

此时,您可以将代码设置为只有 AppController 的一个实例,并且您将始终appController.showView(...)从路由器或任何其他需要在#someElement屏幕部分显示视图的代码调用。

(i have another example of a very simple backbone app that uses an "AppView" (a backbone view that runs the main portion of the app), here: http://jsfiddle.net/derickbailey/dHrXv/9/)

(我还有一个非常简单的主干应用程序示例,它使用“AppView”(运行应用程序主要部分的主干视图),这里是:http: //jsfiddle.net/derickbailey/dHrXv/9/

the closemethod does not exist on views by default, so you need to create one yourself, for each of your views. There are two things that should always be in the close method: this.unbind()and this.remove(). in addition to these, if you are binding your view to any model or collection events, you should unbind them in the close method.

close默认情况下,视图上不存在该方法,因此您需要为每个视图自己创建一个。close 方法中应始终包含两件事:this.unbind()this.remove(). 除此之外,如果您将视图绑定到任何模型或集合事件,您应该在 close 方法中解除它们的绑定。

for example:

例如:


  MyView = Backbone.View.extend({
    initialize: function(){
      this.model.bind("change", this.modelChanged, this);
    },

    modelChanged: function(){
      // ... do stuff here
    },

    close: function(){
      this.remove();
      this.unbind();
      this.model.unbind("change", this.modelChanged);
    }
  });

this will properly clean up all of the events from the DOM (via this.remove()), all of the events that the view itself may raise (via this.unbind()) and the event that the view bound from the model (via this.model.unbind(...)).

这将正确清除 DOM 中的所有事件(via this.remove())、视图本身可能引发的所有事件(via this.unbind())以及视图从模型绑定的事件(via this.model.unbind(...))。

回答by Shaheen Ghiassy

A simpiler way is to add a custom close method on the Backbone.View object

一种更简单的方法是在 Backbone.View 对象上添加自定义关闭方法

Backbone.View.prototype.close = function () {
  this.$el.empty();
  this.unbind();
};

Using the above code you can do the following

使用上面的代码,您可以执行以下操作

var myView = new MyView();

myView.close();

easy peasy.

十分简单。

回答by djabraham

I always nuke the views and sometimes reuse the models. Making sure the views are deallocated can be a pain, if you keep the models around. The models may keep a reference to the view if they are not unbound properly.

我总是对视图进行核处理,有时还会重用模型。如果您保留模型,确保视图被释放可能会很痛苦。如果模型未正确解除绑定,它们可能会保留对视图的引用。

As of Backbone ~0.9.9, binding models with view.listenTo() rather than model.on() allows for easier cleanup through inversion of control (views control bindings as opposed to models). If view.listenTo() is used to bind, then a call to view.stopListening() or view.remove() will remove all bindings. Similar to calling model.off(null, null, this).

从 Backbone ~0.9.9 开始,使用 view.listenTo() 而不是 model.on() 绑定模型允许通过控制反转(视图控制绑定而不是模型)更容易地清理。如果使用 view.listenTo() 进行绑定,则调用 view.stopListening() 或 view.remove() 将删除所有绑定。类似于调用model.off(null, null, this)。

I like to cleanup views by extending the view with a close function that calls sub-views semi-automatically. The subviews must be referenced by properties of the parent or they must be added to an array within the parent called childViews[].

我喜欢通过使用半自动调用子视图的关闭函数扩展视图来清理视图。子视图必须由父视图的属性引用,或者它们必须添加到父视图中名为 childViews[] 的数组中。

Here is the close function that I use..

这是我使用的关闭功能..

// fired by the router, signals the destruct event within top view and 
// recursively collapses all the sub-views that are stored as properties
Backbone.View.prototype.close = function () {

    // calls views closing event handler first, if implemented (optional)
    if (this.closing) {
        this.closing();  // this for custom cleanup purposes
    }

    // first loop through childViews[] if defined, in collection views
    //  populate an array property i.e. this.childViews[] = new ControlViews()
    if (this.childViews) {
        _.each(this.childViews, function (child) {
            child.close();
        });
    }

    // close all child views that are referenced by property, in model views
    //  add a property for reference i.e. this.toolbar = new ToolbarView();
    for (var prop in this) {
        if (this[prop] instanceof Backbone.View) {
            this[prop].close();
        }
    }

    this.unbind();
    this.remove();

    // available in Backbone 0.9.9 + when using view.listenTo, 
    //  removes model and collection bindings
    // this.stopListening(); // its automatically called by remove()

    // remove any model bindings to this view 
    //  (pre Backbone 0.9.9 or if using model.on to bind events)
    // if (this.model) {
    //  this.model.off(null, null, this);
    // }

    // remove and collection bindings to this view 
    //  (pre Backbone 0.9.9 or if using collection.on to bind events)
    // if (this.collection) {
    //  this.collection.off(null, null, this);
    // }
}

Then a view is declared as follows..

然后声明一个视图如下..

views.TeamView = Backbone.View.extend({

    initialize: function () {
        // instantiate this array to ensure sub-view destruction on close()
        this.childViews = [];  

        this.listenTo(this.collection, "add", this.add);
        this.listenTo(this.collection, "reset", this.reset);

        // storing sub-view as a property will ensure destruction on close()
        this.editView = new views.EditView({ model: this.model.edits });
        $('#edit', this.el).html(this.editView.render().el);
    },

    add: function (member) {
        var memberView = new views.MemberView({ model: member });
        this.childViews.push(memberView);    // add child to array

        var item = memberView.render().el;
        this.$el.append(item);
    },

    reset: function () {
        // manually purge child views upon reset
        _.each(this.childViews, function (child) {
            child.close();
        });

        this.childViews = [];
    },

    // render is called externally and should handle case where collection
    // was already populated, as is the case if it is recycled
    render: function () {
        this.$el.empty();

        _.each(this.collection.models, function (member) {
            this.add(member);
        }, this);
        return this;
    }

    // fired by a prototype extension
    closing: function () {
        // handle other unbinding needs, here
    }
});