2014-09-18 8 views
6

Sono abbastanza nuovo per la dorsale e cerco di capire i dettagli delle viste di zombie.Viste zombie della spina dorsale e buone pratiche

Uno zombie è, secondo questa article:

Quando ci leghiamo gli oggetti insieme attraverso eventi, ma non ci preoccupiamo di loro non associare. Finché questi oggetti sono collegati tra loro, e c'è un riferimento nel nostro codice app ad almeno uno di essi, non verranno ripuliti o eliminati. Le perdite di memoria che ne derivano sono come gli zombi dei film - nascosti negli angoli bui, in attesa di saltare fuori e mangiarci a pranzo.

L'articolo menzionate sopra suggerisce di creare un oggetto che gestisce le transizioni tra viste e quindi attuare una funzione di chiusura per rimuovere e separare la vista.

Detto questo, a seconda della situazione, da dove chiamare quella funzione da vicino?

Aggiungo una proprietà nel blocco di inizializzazione della mia vista genitore per mantenere una traccia della vista secondaria. In questo modo sono in grado di chiamare .remove() su di esso prima di sostituirlo con uno nuovo. È una buona pratica o c'è un modo migliore?

anche io non capisco perché la definizione el e poi il rendering con

this.$el.html(this.template(this.model.attributes));

non mi permetterà di non associare la vista mentre funziona come previsto facendo

$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

Per quanto riguarda l'esempio, ho appena creato una semplice app che visualizza un elenco di nomi di sportivi e che mostra maggiori dettagli quando si fa clic su un nome.

Ecco il codice e un fiddle di lavoro:

html

<script id="nameListTemplate" type="text/template"> 
    <%= first %> <%= last %> 
</script> 
<script id="sportsManDetailsTemplate" type="text/template"> 
    <ul> 
     <li><%= first %></li> 
     <li><%= last %></li> 
     <li><%= age %></li> 
     <li><%= sport %></li> 
     <li><%= category %></li> 
    </ul> 
    <button class="test">Test</button> 
</script> 
<div id="sportsMenName"></div> 
<div id="sportsManDetails"></div> 

JS

modello e la raccolta

var app = app || {}; 

app.SportsManModel = Backbone.Model.extend({}); 

app.SportsMenCollection = Backbone.Collection.extend({ 
    model: app.SportsManModel 
}); 

NameView

app.NameView = Backbone.View.extend({ 
    tagName: 'li', 
    className: 'sportsMan', 
    template: _.template($('#nameListTemplate').html()), 

    initialize: function(){ 
     this.sportsManDetailsView; 
    }, 

    events: { 
     'click': 'showSportsManDetails' 
    }, 

    showSportsManDetails: function(e){ 
     if (typeof this.sportsManDetailsView !== 'undefined'){ 
      this.sportsManDetailsView.remove(); 
     } 
     this.sportsManDetailsView = new app.SportsManDetailsView({ 
      model: this.model 
     }) 
    }, 

    render: function(){ 
     this.$el.append(this.template(this.model.attributes)); 
     return this; 
    } 
}); 

NameListView

app.NameListView = Backbone.View.extend({ 
    el: '#sportsMenName', 

    initialize: function(sportsMen){ 
     this.collection = new app.SportsMenCollection(sportsMen); 
     this.render(); 
    }, 

    render: function(){ 
     this.collection.each(function(sportsMen){ 
      this.renderContact(sportsMen); 
     }, this); 
    }, 

    renderContact: function(sportsMen){ 
     var nameView = new app.NameView({ 
      model: sportsMen 
     }); 
     this.$el.append(nameView.render().el); 
    } 
}); 

SportsManDetailsView

app.SportsManDetailsView = Backbone.View.extend({ 
    // doesn't work if I use el in conjunction with 
    // this.$el.html(this.template(this.model.attributes)); 
    // el: '#sportsManDetails', 
    template: _.template($('#sportsManDetailsTemplate').html()), 

    initialize: function(){ 
     this.render(); 
    }, 

    events: { 
     'click .test': 'test' 
    }, 

    test: function(){ 
     alert('test'); 
    }, 

    render: function(){      
     // that does not work 
     //this.$el.html(this.template(this.model.attributes)); 

     // is this good practice? 
     $('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes))); 
    } 
}); 

app.js

var sportsMen = [ 
    {first: 'Quentin', last: 'Tarant', age: '34', sport: 'bike', category: '- 90kg'}, 
    {first: 'Aymeric', last: 'McArthur', age: '54', sport: 'jetski', category: '200HP'}, 
    {first: 'Peter', last: 'TheFat', age: '45', sport: 'curling', category: 'dunno'}, 
    {first: 'Charles', last: 'Martel', age: '21', sport: 'Moto', category: 'MX 250cc'}, 
]; 

$(function(){ 
    new app.NameListView(sportsMen); 
}); 

risposta

6

Proprio come si sta scoprendo, spina dorsale si considera più di una libreria di un quadro - lascia molte domande e modelli di progettazione lasciati allo sviluppatore.

Il termine "vista zombi" viene utilizzato per designare viste che sono ancora legate a qualcosa (e quindi vive) quando si pensa che siano morte. Di solito c'è un riferimento rimanente alla vista da un model.on chiamata o simile. Fondamentalmente una forma specifica di perdita di memoria.

Per gestire il ciclo di vita della vista, è possibile utilizzare una vista genitore, ma è una pratica comune farlo da un router. Il router viene utilizzato per rimuovere vecchie visualizzazioni e istanziarne di nuove su un evento di percorso. Ecco un frammento di come spesso mi realizzare questo:

render: function(){ 
    this.mainView && this.mainView.remove();     // if there is already a view, remove it 
    this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent(); // instantiate the new view 
    this.mainView.render(); 
    this.mainView.$el.appendTo('#main-content');    // append it 
} 

Alcune cose da notare:

  1. Senza mettere esplicitamente remove su una visione, la vostra applicazione sarà vulnerabile a perdite di memoria. Questo perché gli eventi e le proprietà della vista esistono ancora in background. Ad esempio, se rimuovi la prima riga dell'esempio di cui sopra, perderò il mio riferimento al precedente this.mainView, ma gli eventi continuano a utilizzare la memoria. Ciò avrà un effetto sulla tua app nel tempo.
  2. Nota che sto usando appendTo nell'ultima riga. Quando chiami remove su una vista, viene rimosso l'intero elemento, così come i suoi eventi. Se avessi fatto questo semplicemente:

    this.mainView = new SomeOtherKindOfViewDeterminedBySomeEvent({ el: '#main-content' })

    Poi, dopo che io chiamo remove su this.mainView, #main-content saranno stati rimossi dal DOM, in modo da non possono più utilizzare tale selettore. Aggiungendolo, tengo quello #main-content come un segnaposto, quindi posso continuare ad aggiungere viste ad esso. Questo è quello che si sta vedendo quando si tenta di dissipare SportsManDetailsView quindi renderlo di nuovo.

Per quanto riguarda le vostre domande, in questo modo:

$('#sportsManDetails').html(this.$el.html(this.template(this.model.attributes)));

È non buona pratica. Questo primo è che hai usato l'oggetto jQuery globale, che sconfigge l'approccio di Backbone delle viste incapsulate. In secondo luogo, gli eventi sono ancora attivi nel DOM da precedenti visualizzazioni, che portano a perdite di memoria. Puoi vederlo quando fai clic sul pulsante Test: la funzione di gestione verrà attivata ogni volta che hai creato un'istanza di SportsManDetailsView (la seconda volta, il messaggio di avviso verrà visualizzato due volte, poi tre volte, ecc.)

È necessario affidarsi a una vista padre o router per gestire tale interazione. Quello, o mantieni il tuo SportsManDetailsView associato all'elemento #sportsManDetails e non lo rimuovi mai. Quindi, quando si verifica l'evento click su NameView, attivare il trigger del trigger del modello su un evento. Quindi il tuo SportsManDetailsView può ascoltare l'evento nella raccolta corrispondente e ripetere il rendering di conseguenza. Abbraccia gli eventi di Backbone! JavaScript è un linguaggio guidato dagli eventi e non dimenticarti mai di avere quelli della tua artiglieria.

Ho aggiornato il vostro JSFiddle per dimostrare alcune delle cose di cui ho parlato.

+0

Questo è veramente fantastico! Mi hai illuminato su questo argomento Grazie mille – Buzut

+0

Nessun problema! Hai fatto una buona domanda che meritava una buona spiegazione. Buona fortuna per i tuoi viaggi Backbone! – ncksllvn

+0

Non penso che sarà impeccabile, ma fa parte del gioco Grazie ancora :) – Buzut

0
el: '#sportsManDetails', 

non può funzionare se non si carica jQuery al momento si crea un costruttore della vista.

Accertarsi che siano presenti jQuery caricato prima di questa linea

app.SportsManDetailsView = Backbone.View.extend({ 

si può controllare con

console.log(jQuery.fn.jquery);` 
+0

Eppure, aggiungendo 'console.log (jQuery.fn.jquery);' prima di 'app.SportsManDetailsView = Backbone.View.extend ({' output 2.1.1, che attesta jQuery è ben caricato ... – Buzut