Search code examples
backbone.jsrequirejslazy-loadingamdjs-amd

AMD / Require JS - How to load files on demand only


I have some AMD / Require.js questions. I'm pretty new to modular js and AMD, so it would be great if someone could give me a hand with this.

Let's say that I have 2 distinct areas on my system, defined as modules ("define()" function - I'm using Backbone, btw )

  • Books
    • BookItemView.js
    • BookModel.js
    • BookCollection.js
  • DVDS
    • DvdItemView.js
    • DvdModel.js
    • DvdCollection.js

I noticed that if the user is currently accessing the "Books" section of the site, the files from the DVD section are included by Require.js as well. I've got the following question:

  • Is there some way to include only the files related to the active site section? There are some situations where the user is not interested to see the DVD list or even not allowed to do so. I would like to keep the server from making a unnecessary HTTP Request. Is there any chance to do this using Require.js?

Any ideas would be nice.


Solution

  • This is possible, but depends heavily upon how you structure your JS application. Also, I'm assuming you have a Backbone app here. There are different approaches for different frameworks.

    For instance, you may have an app.js that looks something like this:

    define([
        'DvdItemView', 'DvdModel', 'DvdCollection', 
        'BookItemView', 'BookModel', 'BookCollection'
    ], function(DvdView, DvdModel, DvdCol, BookView, BookModel, BookCol) {
    
       // router declarations go here
    
    });
    

    Because you 'define' this module as depending upon the above modules, they will always be loaded before running the router code. As @alexndm points out, if you run r.js on this you can actually combine all of your modules into one.

    However as you mention, once the application gets to be a certain size what you want is to lazy-load different parts depending upon what the user is interacting with. This is more of an application concern, and RequireJS can only help you so much in this regard. I believe the best way to do this is in the router.

    routes: {
        '/books': 'booksList',
        '/books/:id': 'booksDetail',
        '/dvds': 'dvdList',
        '/dvds/:id': 'dvdDetail'
    },
    
    booksList: function() {
        if (!booksDepsLoaded) {
            // Here we go and load the appropriate files
            require(['BooksArea'], function(BooksArea) {
                // BooksArea.Model, BooksArea.Collection, etc...
                // do stuff to render a book list..
            })
        }
    },
    
    ...
    

    This is very naive pseudo code. You will likely want to write some generic code to handle routes that still need dependencies to be loaded, and error handling, etc.

    The important concept to grasp though, is you need to have the routes defined before the application/router is started - and then you need async code to handle fetching modules.