(warning: my answer became a tl;dr treatise)
I had some of these same questions early on, and have done my homework in order to build a few pretty complex apps, so I'll offer my perspective.
The big challenge with learning backbone is that it is so unopinionated and can be (and is) used in so many different ways that it's hard to figure out how to do something "right" or at least in a good way when you're starting out. There isn't just one true way to use backbone, but its flexibility makes it an awesome structure for almost any app, and hopefully I can help provide some guidance. (I could probably attach "IMO" to every sentence in here).
First, my understanding of backbone views
In backbone apps, there are a lot of useful ways to use views. I generally see a few overlapping types of views in my apps:
I typically have one or more "root-level" views. Root-level views are often a place to initialize, render, and keep references to child views that handle specific parts of the page. The el
of a root-level view is often "body" or another high level element within the body. In some cases, a root-level view has its own HTML to observe and/or render. In others, a root-level view may have no el
at all and just manages child views. I keep a reference to each root-level view (there is often only one) in a global 'app' namespace object.
In addition to "root-level" views, there are typically "child views". A child view is initialized and rendered by a "parent" view, which may be a root-level view or another child view. Parent views are in charge of initializing, rendering, hiding, showing, and/or destroying their children as the app requires. Sometimes a parent view may keep track of a variable number of instances of a child View (e.g., a PlaylistView has N SongViews). Often, parents maintain references to children, but sometimes it is unnecessary (more on this below).
In addition to the 'root-level/parent/child' paradigm, I tend to see views fit into one of two categories: (1) static: meaning that once the view is initialized, the view and its el
stick around, even if stuff changes inside it; and (2) dynamic, which come and go, based on various events. Typically, my root-level views are always static. They also typically correspond to an existing DOM element, e.g., 'body' or '#my-div'. Child views are often dynamic, but may be static as well. (Tips: use el: '#element-id'
to use an existing DOM element as el
when declaring a static View. Dynamic views typically don't specify an existing el
; they use tagName
id
and className
to describe the element that the dynamic view will generate.)
Views essentially have 3 functions: (1) render themselves and their children when told to do so by their parents or in response to events (or in the case of a root-level view, when initialized by a router or a 'main' function, etc.), (2) respond to UI events from DOM elements within their el
(but not within the el
of any child view) by updating models or collections or triggering custom Backbone events, and (3) observe and respond to Backbone (model, collection, etc.) events that require something to be rendered or changed within their el (but not within the el of any child view.) One sometimes-useful trick is that child views can trigger events on themselves (this.trigger('customEvent')
) that parent views can observe ( childView.on('customEvent', this.handler, this)
).
For additional interesting perspectives on backbone view patterns see: this and this.
Now in that context, on to questions
1) Fear of garbage collection, scope, and of memory leaks
If you instantiate a child view as a local var in a parent's render (or other) method and render it, and then the function goes out of scope, I can understand your fear of garbage collection or that the view won't be able to do what it needs to do. No need to fear the garbage collector, just the zombies. If your view has any event handlers whatsoever, whether UI Event handlers declared in the "events" declaration, or bindings to other Backbone objects' events, or other DOM-based event listeners, your view will not be garbage collected even though you don't have a reference to it anymore--it will still exist in memory and respond to the events. On the other hand, if a view doesn't have any event handlers, then its only job is to render an element, so who cares if the javascript object that rendered it sticks around--it will probably be garbage collected because it should be. See this, for a great understanding of js garbage collection in general and how it relates to Backbone.js.
The bigger concern is of Zombie views. If views are to be removed from the DOM and essentially discarded at some point in your app, then make sure they either fully remove themselves or that parent views should keep a reference to them and remove them. And don't re-create and replace views that have already been created and not properly removed. Removal is accomplished by calling .remove() on the view, plus unbinding any external Backbone events that you were previously bound using on(...)
using off(...)
. Recent versions (1.0+) of Backbone make this issue more easily addressed by adding the "listenTo" and "stopListening" methods to the View prototype. Understand and use these methods instead of on/off if you're dynamically adding the views to and from the DOM. Tip: Setting up a a hacky jquery "remove" event like this one can easily enable views to autonomously remove and clean-up themselves when their el
is removed from the DOM (in the event that there isn't an event in your app flow that can serve the same purpose).
2) Should child views be maintained as data members of parent views?
It depends. I don't think parent views being aware of their child views for limited purposes violates any golden principles of MVC. Sometimes, a parent having member references to specific child view instances is a great way to manage child views if/when you need it. As I indicated, sometimes parent views respond to events that require them to render, re-render, hide, or remove their child views. Sometimes they may want to listen to events that child views trigger on themselves. Parents, however, shouldn't get too involved in anything inside of their child views' el
's though.
That said, don't overuse these types of references. Many times, you will not need to use references to child views, because the children can take care of themselves. As I mentioned, views, once rendered, should only A) watch for UI events inside their el (but typically not inside any child view's el) and update models or collections or trigger events in response to these UI events, or B) watch for events from other backbone objects (typically models or collections or other views) and take actions (e.g., update their own UI elements) in response. In many instances, a view can take care of itself and even remove itself. If another View or other Backbone object cares about a UI event happening in your view, update a model or trigger an event on the view and let them observe it. Likewise, if something outside your view requires updated rendering in your view, listen for a change to a model or wait on a corresponding custom event. As a general principle, views should be blissfully unaware of each other, with the exception of parents minding their children when it makes sense.
3) Should child views maintain references to parent views?
No. Never. I can't think of a single scenario where you would need to accomplish something through a reference to a parent that couldn't be accomplished by either changing a model that the parent is observing, or triggering an event (e.g., a custom event saying "hey, X happened") on the child view itself or on another backbone "Events"-based object. In Backbone, I use models to represent both my data AND my state. So if something happens in a view that changes the state of my application, I then change the corresponding state attribute of a model and let other views (including the parent) listen to the automatic "change" event if they care. I also use a global "vent" bus-like object (just a basic javascript object that extends Backbone.Events) for triggering and listening on events across the app, and sometimes I trigger events on Views themselves to let parent objects know that something happened. Whatever works, while keeping your architecture as untangled as possible.
4) I don't really want top start throwing listeners around everywhere.
Well I guess one good thing about backbone is that you don't have to, but realize that the Observer pattern (ie events & listeners) and loose coupling are at the heart of backbone's flavor of MVC (notice that every single Backbone class extends Events?), and most people use it accordingly.
References?
I strongly recommend the PeepCode tutorials unless you feel you're already at a pretty advanced level. 12 bucks a piece, but you need to start with the first one or the second & third won't be very useful.
Also, here's a nice overview.
The end