At 500px, we use Backbone Marionette for the majority of our web application. Marionette presents a fairly straightforward framework which is easy to jump into, but while it’s more well defined than Backbone on its own, it’s not very opinionated on how exactly you should structure your app. Over time, we’ve developed best practices which […]
At 500px, we use Backbone Marionette for the majority of our web application. Marionette presents a fairly straightforward framework which is easy to jump into, but while it’s more well defined than Backbone on its own, it’s not very opinionated on how exactly you should structure your app. Over time, we’ve developed best practices which help us write sane, maintainable Marionette code.
There is one basic principle that should result in mostly reasonable Marionette components:
You should always be able to re-render.
That is, calling render() should result in no change to the DOM. The DOM should always be a reflection of the state of the view instance. To put it another way, if we were to clone the view instance in its current state and then render it on another part of the page, both instances should render identical markup. We will identify a few rules and best practices that follow naturally from this principle and which will help you achieve it.
When this happens, some of the state of your component is actually stored only in the DOM, and re-rendering the component will cause this information to be lost.
How can we avoid this? Do as much as you possibly can in the template, and if absolutely necessary, manipulate the DOM only in the onRender function (which is called by Marionette after rendering). You can make use of templateHelpers to make this a bit easier. Any time you need to change the state of the DOM, you should modify a view instance variable and re-render the view by calling render().
You should avoid storing state in the DOM at all costs. The DOM should reflect the state of the view instance, and you should never need to query the DOM for information. If some state is stored exclusively in the DOM (in the form of “selected” elements, checkbox states, user re-ordered items, etc.), then calling render() will cause us to lose that state, breaking our basic principle.
There are a lot of obvious and oft-mentioned benefits to breaking code up into smaller chunks. It’s easier to read and maintain, promotes code reuse, is easier to test, helps parallelize development… the list goes on. As it relates to our rule of always rendering instead of directly updating the DOM, it helps us keep the scope of renders under control and render only what is required.
If your app has distinct pages, don’t overcomplicate things by listening for events and updating the page piece at a time — just use the router. Even if you have some common elements, like navigation, you can pull those into their own view components and include them in each page. You can think of this as applying the Just render rule to the entire page. It’s much easier to follow and understand your code if there is one, clean path to building each page or screen.
Of course, there are always exceptions to the rule. It is not always possible to avoid updating the DOM outside of render(). For example, sometimes re-rendering will result in a “jump” in the UI, or perhaps the loss of a scroll position. Rendering for each change also means you can’t use any transitions. Try to keep these situations limited to small sub-views in order to contain the damage, and it doesn’t hurt to leave a comment explaining why you’re breaking the general rule of thumb.
If you follow theses basic guidelines, and always ask yourself “if I re-rendered this view right now, would anything change?”, you’ll be on your way to writing some reasonably maintainable Backbone Marionette views. They seem straightforward, but even when you know them, they’re easy to ignore when deadlines are looming and crunch time is upon you. Your future self (and coworkers) will thank you if you resist that urge.
A basic principle for writing sane Backbone Marionette code was originally published in 500px Engineering Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.