Das ist eine gute Frage. Backbone ist großartig, weil es keine Annahmen macht, aber es bedeutet, dass Sie solche Dinge selbst implementieren müssen. Nachdem ich meine eigenen Sachen durchgesehen habe, stelle ich fest, dass ich (irgendwie) eine Mischung aus Szenario 1 und Szenario 2 verwende. Ich glaube nicht, dass ein viertes magisches Szenario existiert, weil einfach alles, was Sie in Szenario 1 und 2 tun, sein muss getan.
Ich denke, es wäre am einfachsten zu erklären, wie ich mit einem Beispiel umgehen möchte. Angenommen, ich habe diese einfache Seite in die angegebenen Ansichten unterteilt:
Angenommen, der HTML-Code ist nach dem Rendern ungefähr so:
<div id="parent">
<div id="name">Person: Kevin Peel</div>
<div id="info">
First name: <span class="first_name">Kevin</span><br />
Last name: <span class="last_name">Peel</span><br />
</div>
<div>Phone Numbers:</div>
<div id="phone_numbers">
<div>#1: 123-456-7890</div>
<div>#2: 456-789-0123</div>
</div>
</div>
Hoffentlich ist es ziemlich offensichtlich, wie der HTML-Code mit dem Diagramm übereinstimmt.
Das ParentView
hält 2 Kind Ansichten, InfoView
und PhoneListView
sowie ein paar zusätzliche divs, von denen eines , #name
muss an einem bestimmten Punkt eingestellt werden. PhoneListView
Enthält eigene untergeordnete Ansichten, eine Reihe von PhoneView
Einträgen.
Also weiter zu Ihrer eigentlichen Frage. Ich gehe mit der Initialisierung und dem Rendern je nach Ansichtstyp unterschiedlich um. Ich teile meine Ansichten in zwei Typen ein, Parent
Ansichten und Child
Ansichten.
Der Unterschied zwischen ihnen ist einfach: Parent
Ansichten enthalten untergeordnete Ansichten, Child
Ansichten jedoch nicht. Also in meinem Beispiel ParentView
und PhoneListView
sind Parent
Ansichten, während InfoView
und die PhoneView
Einträge Child
Ansichten sind.
Wie ich bereits erwähnt habe, besteht der größte Unterschied zwischen diesen beiden Kategorien darin, wann sie rendern dürfen. In einer perfekten Welt möchte ich, dass Parent
Ansichten immer nur einmal gerendert werden. Es liegt an ihren untergeordneten Ansichten, das erneute Rendern durchzuführen, wenn sich die Modelle ändern. Child
Ansichten hingegen erlaube ich, sie jederzeit neu zu rendern, da sie keine anderen Ansichten haben, die sich auf sie stützen.
Für Parent
Ansichten möchte ich, dass meine initialize
Funktionen einige Dinge tun:
- Initialisieren Sie meine eigene Ansicht
- Machen Sie meine eigene Ansicht
- Erstellen und initialisieren Sie untergeordnete Ansichten.
- Weisen Sie jeder untergeordneten Ansicht ein Element in meiner Ansicht zu (z. B. das
InfoView
würde zugewiesen werden #info
).
Schritt 1 ist ziemlich selbsterklärend.
Schritt 2, das Rendern, wird ausgeführt, sodass alle Elemente, auf die sich die untergeordneten Ansichten stützen, bereits vorhanden sind, bevor ich versuche, sie zuzuweisen. Auf diese Weise weiß ich, dass alle Kinder events
richtig eingestellt sind, und ich kann ihre Blöcke so oft neu rendern, wie ich möchte, ohne mir Sorgen machen zu müssen, dass etwas neu delegiert werden muss. Ich habe hier eigentlich render
keine kindlichen Ansichten, ich erlaube ihnen, das in ihren eigenen zu tun initialization
.
Die Schritte 3 und 4 werden tatsächlich zur gleichen Zeit ausgeführt, zu der ich el
beim Erstellen der untergeordneten Ansicht übergebe. Ich möchte hier ein Element übergeben, da ich der Meinung bin, dass die Eltern bestimmen sollten, wo das Kind nach seiner eigenen Ansicht seinen Inhalt ablegen darf.
Beim Rendern versuche ich, es für Parent
Ansichten ziemlich einfach zu halten . Ich möchte, dass die render
Funktion nur die übergeordnete Ansicht rendert. Keine Ereignisdelegation, kein Rendern von untergeordneten Ansichten, nichts. Nur ein einfaches Rendern.
Manchmal funktioniert das aber nicht immer. In meinem obigen Beispiel muss das #name
Element beispielsweise jedes Mal aktualisiert werden, wenn sich der Name im Modell ändert. Dieser Block ist jedoch Teil der ParentView
Vorlage und wird nicht von einer dedizierten Child
Ansicht verarbeitet. Deshalb arbeite ich daran. Ich werde eine Art subRender
Funktion erstellen , die nur den Inhalt des #name
Elements ersetzt und nicht das gesamte #parent
Element in den Papierkorb werfen muss. Dies mag wie ein Hack erscheinen, aber ich habe wirklich festgestellt, dass es besser funktioniert, als sich Gedanken über das erneute Rendern des gesamten DOM und das erneute Anbringen von Elementen und dergleichen machen zu müssen. Wenn ich es wirklich sauber machen wollte, würde ich eine neue Child
Ansicht (ähnlich der InfoView
) erstellen , die den #name
Block handhaben würde .
Bei Child
Ansichten ist die Ansicht initialization
ziemlich ähnlich Parent
, nur ohne dass weitere Child
Ansichten erstellt werden müssen. So:
- Initialisieren Sie meine Ansicht
- Setup bindet das Abhören von Änderungen an dem Modell, das mir wichtig ist
- Machen Sie meine Ansicht
Child
Das Rendern von Ansichten ist ebenfalls sehr einfach. Rendern Sie einfach den Inhalt von my und legen Sie ihn fest el
. Wieder kein Durcheinander mit der Delegation oder so etwas.
Hier ist ein Beispielcode, wie mein ParentView
aussehen könnte:
var ParentView = Backbone.View.extend({
el: "#parent",
initialize: function() {
// Step 1, (init) I want to know anytime the name changes
this.model.bind("change:first_name", this.subRender, this);
this.model.bind("change:last_name", this.subRender, this);
// Step 2, render my own view
this.render();
// Step 3/4, create the children and assign elements
this.infoView = new InfoView({el: "#info", model: this.model});
this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model});
},
render: function() {
// Render my template
this.$el.html(this.template());
// Render the name
this.subRender();
},
subRender: function() {
// Set our name block and only our name block
$("#name").html("Person: " + this.model.first_name + " " + this.model.last_name);
}
});
Sie können meine Implementierung subRender
hier sehen. Dadurch , dass Änderungen gebunden subRender
statt render
, habe ich nicht zu befürchten Strahlen weg und den Wiederaufbau des gesamten Blocks.
Hier ist ein Beispielcode für den InfoView
Block:
var InfoView = Backbone.View.extend({
initialize: function() {
// I want to re-render on changes
this.model.bind("change", this.render, this);
// Render
this.render();
},
render: function() {
// Just render my template
this.$el.html(this.template());
}
});
Die Bindungen sind hier der wichtige Teil. Durch die Bindung an mein Modell muss ich mich nie mehr darum kümmern, render
mich selbst manuell anzurufen. Wenn sich das Modell ändert, wird dieser Block selbst neu gerendert, ohne andere Ansichten zu beeinflussen.
Das PhoneListView
wird dem ähnlich sein ParentView
, Sie benötigen nur ein wenig mehr Logik sowohl in Ihrer initialization
als auch in Ihren render
Funktionen, um Sammlungen zu verwalten. Wie Sie mit der Sammlung umgehen, liegt ganz bei Ihnen, aber Sie müssen zumindest die Sammlungsereignisse abhören und entscheiden, wie Sie rendern möchten (Anhängen / Entfernen oder einfach das Rendern des gesamten Blocks). Ich persönlich möchte neue Ansichten anhängen und alte entfernen, nicht die gesamte Ansicht neu rendern.
Das PhoneView
wird fast identisch mit dem sein InfoView
, nur das Hören der Modelländerungen, die es interessiert.
Hoffentlich hat dies ein wenig geholfen. Bitte lassen Sie mich wissen, wenn etwas verwirrend oder nicht detailliert genug ist.