it-swarm.it

Smaltimento di oggetti vista e modello in Backbone.js

Qual è il modo più efficiente per disporre le istanze del modello/vista quando non sono necessarie?

Di solito, inserisco tutta la logica nel controller/router. È quello che decide, quali viste dovrebbero essere create e quali modelli dovrebbero essere forniti a loro. Di solito, ci sono alcune funzioni del gestore, corrispondenti a diverse azioni o percorsi dell'utente, in cui creo nuove istanze di visualizzazione ogni volta che viene eseguito un gestore. Ovviamente, ciò dovrebbe eliminare tutto ciò che ho precedentemente memorizzato nell'istanza della vista. Tuttavia, ci sono alcune situazioni in cui alcune viste mantengono gli stessi gestori di eventi DOM e non si separano correttamente, con il risultato che tali istanze vengono mantenute in vita. Vorrei che ci fosse un modo corretto di distruggere le istanze di visualizzazione, ad esempio quando la loro el (rappresentazione DOM) viene staccata o espulsa dal DOM

64
user802232

sei sulla strada giusta. dovresti avere un oggetto che controlli il ciclo di vita delle tue viste. non mi piace metterlo a mio avviso. mi piace creare un oggetto separato per questo.

la cosa che devi fare è sciogliere gli eventi quando necessario. per fare ciò, è una buona idea creare un metodo di "chiusura" su tutte le viste e utilizzare l'oggetto che controlla il ciclo di vita di tutto per chiamare sempre il metodo di chiusura.

per esempio:


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

a questo punto, imposteresti il ​​tuo codice per avere solo un'istanza di AppController e chiameresti sempre appController.showView(...) dal tuo router o qualsiasi altro codice che deve mostrare una vista in #someElement porzione dello schermo.

(ho un altro esempio di un'app backbone molto semplice che utilizza un "AppView" (una vista backbone che esegue la parte principale dell'app), qui: http://jsfiddle.net/derickbailey/dHrXv/9 / )

il metodo close non esiste nelle viste per impostazione predefinita, quindi devi crearne uno tu stesso, per ciascuna delle tue viste. Ci sono due cose che dovrebbero essere sempre nel metodo close: this.unbind() e this.remove(). oltre a questi, se stai vincolando la tua vista a qualsiasi modello o evento di raccolta, dovresti separarli nel metodo di chiusura.

per esempio:


  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);
    }
  });

questo pulirà correttamente tutti gli eventi dal DOM (tramite this.remove()), tutti gli eventi che la vista stessa può generare (tramite this.unbind()) e l'evento che la vista ha associato dal modello (tramite this.model.unbind(...)).

77
Derick Bailey

Un modo più semplice è aggiungere un metodo di chiusura personalizzato sull'oggetto Backbone.View

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

Utilizzando il codice sopra è possibile effettuare le seguenti operazioni

var myView = new MyView();

myView.close();

vai tranquillo.

13
Shaheen Ghiassy

Nuke sempre le visualizzazioni e talvolta riutilizzo i modelli. Accertarsi che le viste siano deallocate può essere una seccatura, se si tengono in giro i modelli. I modelli possono mantenere un riferimento alla vista se non sono correttamente associati.

A partire da Backbone ~ 0.9.9, i modelli di associazione con view.listenTo () anziché model.on () consentono una pulizia più semplice attraverso l'inversione del controllo (visualizza i binding di controllo rispetto ai modelli). Se view.listenTo () viene utilizzato per eseguire il bind, una chiamata a view.stopListening () o view.remove () rimuoverà tutti i bind. Simile alla chiamata model.off (null, null, this).

Mi piace pulire le viste estendendo la vista con una funzione di chiusura che chiama semi-viste semi-automatiche. Le viste secondarie devono essere referenziate dalle proprietà del genitore o devono essere aggiunte ad un array all'interno del genitore chiamato childViews [].

Ecco la funzione di chiusura che uso ..

// 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);
    // }
}

Quindi una vista viene dichiarata come segue.

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
    }
});
6
djabraham