Large backbone.js web app organization

20,141

Solution 1

I recently worked on a Backbone project called GapVis (code here, rendered content here). I don't know if it's "really large", but it's big-ish and relatively complex - 24 view classes, 5 routers, etc. It might be worth taking a look, though I don't know that all my approaches will be relevant. You can see some of my thinking in the long intro comment in my main app.js file. A few key architectural choices:

  • I have a singleton State model that holds all current state info - the current view, what model ids we're looking at, etc. Every view that needs to modify application state does it by setting attributes on the State, and every view that needs to respond to the state listens to that model for events. This is even true for views that modify state and update - the UI event handlers in events never re-render the view, this is done instead through binding render functions to the state. This pattern really helped to keep views separate from each other - views never call a method on another view.

  • My routers are treated like specialized views - they respond to UI events (i.e. typing in a URL) by updating the state, and they respond to state changes by updating the UI (i.e. changing the URL).

  • I do several things similar to what you're proposing. My namespace has an init function similar to yours, and a settings object for constants. But I put most of the model and view classes in the namespace as well, because I needed to refer to them in multiple files.

  • I use a registration system for my routers, and considered one for my views, as a nice way to keep the "master" classes (AppRouter and AppView) from having to be aware of every view. In the AppView case, though, it turned out that order of child views was important, so I ended up hard-coding those classes.

I'd hardly say that this was the "right" way to do things, but it worked for me. I hope that's helpful - I also had trouble finding visible-source examples of large projects using Backbone, and had to work out most of this as I went along.

Solution 2

These 2 resources helped me to setup my backbone apps on a solid basement:

Solution 3

I namespace similar to what you're doing (at least for the classes part) and all my models, views, and controllers look like this:

views/blocks.js:

(function(cn){
    cn.classes.views.blocks = cn.classes.views.base.extend({

        events: {},

        blocksTemplate: cn.helpers.loadTemplate('tmpl_page_blocks'),

        initialize: function(){
        },

        render: function(){
            $(this.el).html(this.blocksTemplate());
        },

        registerEvents: function(){},
        unregisterEvents: function(){}
    });
})(companyname);

My JavaScript namespace looks like this, though I do improve upon it every time I build a new app:

 companyname:{                                                                                                                                                                                 
   $: function(){},      <== Shortcut reference to document.getElementById                                                                                                                      
   appView: {},          <== Reference to instantiated AppView class.                                                                                                                           
   classes = {           <== Namespace for all custom Backbone classes.                                                                                                                         
     views : {},                                                                                                                                                                                
     models : {},                                                                                                                                                                               
     collections: {},                                                                                                                                                                           
     controllers : {},                                                                                                                                                                          
     Router: null                                                                                                                                                                               
   },                                                                                                                                                                                           
   models: {},          <== Instantiated models.                                                                                                                                                
   controllers: {},     <== Instantiated controllers.                                                                                                                                           
   router: {},          <== Instantiated routers.                                                                                                                                               
   helpers: {},         <== Reusable helper platform methods.                                                                                                                                   
   currentView: {},     <== A reference to the current view so that we can destroy it.                                                                                                          
   init: function(){}   <== Bootstrap code, starts the app.                                                                                                                           
 } 

Anything I want all my views to have, I put in the base view. My controller will call registerEvents on any new view it creates (after render) and unregisterEvents on a view right before it kills it. Not all views have these two extra methods so it first checks for the existence.

Don't forget that all views come with a this.el.remove(); built in. Which not only kills the views container element but unbinds all events attached to it. Depending on how you are creating your views through your controller you may not actually want to kill the element and do this.el.unbind() instead to unbind all events.

Solution 4

In fact, in different ways have advantages and disadvantages of different ways.The most important thing is to find a suitable way of organizing files.The following is the organization of the project I am currently doing. This way the focus will be the same module-related files are placed in a folder. For example: the people module, this module all files are placed in modules / base / people directory. After updating and maintenance of this module, only need to focus on the files in this directory on the line, will not affect files outside the directory, and improved maintainability.

I hope my answer can give you some help, I hope you some valuable advice.

enter image description here

Share:
20,141
user527480
Author by

user527480

Updated on July 08, 2022

Comments

  • user527480
    user527480 almost 2 years

    I'm currently working on a large web app built on backbone.js and have been having a lot of issues with organization, "zombies," etc. so I've decided to do a major refactor of code. I've already written a bunch of helper functions for dealing with the "zombies"; however, I'd like to start from the very beginning and create a nice structure/organization to the code. I haven't found many great tutorials/examples on large-scale backbone.js organization so I've sort of started from scratch and would like to see if I can get some opinions on where I've begun.

    I've obviously set up my code within a global namespace; but I'd also like to keep that namespace rather clean. My main app.js keeps the class files themselves separate from the global namespace; you can register a class (so that it can be instantiated) by using the reg() function and the inst() function instantiates a class from the classes array. Thus, besides the 3 methods, the MyApp namespace only has Router, Model and View:

    var MyApp = (function () {
    
        var classes = {
            Routers: {},
            Collections: {},
            Models: {},
            Views: {}
        };
    
        methods = {
    
            init: function () {
                MyApp.Router = MyApp.inst('Routers', 'App');
                MyApp.Model = MyApp.inst('Models', 'App');
                MyApp.View = MyApp.inst('Views', 'App');
                Backbone.history.start();
            },
    
            reg: function (type, name, C) {
                classes[type][name] = C;
            },
    
            inst: function (type, C, attrs) {
                return new classes[type][C](attrs || {});
            }
    
        };
    
        return methods;
    
    }());
    
    $(MyApp.init);
    

    Within the Models, Collections, Routers and Views, I work as usual but then need to register that class at the end of the file so that it could be instantiated at a later point (without cluttering the namespace) with:

    MyApp.reg('Models', 'App', Model);
    

    Does this seem like an unnecessary way to organize code? Do others have better examples of how to organize really large projects with many Routers, Collections, Models and Views?