Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 581
darksoulsong Avatar asked Dec 30 '13 20:12

darksoulsong


4 Answers

Check out require-lazy, a little library that plugs both on runtime and compile time (r.js) I wrote to do what you ask. The basic usage would be:

(from a main.js script:)

define(["lazy!modules/books", "lazy!modules/dvds"], function(books, dvds) {
    ...
});

The modules/books and modules/dvds scripts would be as usual. The books and dvds variables in the definitions function above are actually promises for the actual modules. The modules themselfes do not get loaded upfront. Then, somewhere in main.js, the code decides which view to display:

if( view === "books" ) {
    books.get().then(function(realBooks) {
       ...
    });
}
else if( view === "dvds" ) {
    dvds.get().then(function(realDvds) {
       ...
    });
}

It supports grunt and bower too.

like image 60
Nikos Paraskevopoulos Avatar answered Sep 27 '22 17:09

Nikos Paraskevopoulos


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.

like image 38
Jon Jaques Avatar answered Sep 27 '22 17:09

Jon Jaques


It's actually possible to do this with a slightly different project structure.

James Burke has a repo that details how to do it here, https://github.com/requirejs/example-multipage-shim

The basic idea is that you have multiple "main.js" files that load the dependencies for different areas of your site.

So, in your case you may have main-books and main-dvd files.

In your HTMl for the different areas, simply load your common modules that you'd need for both areas, and then load the specific main file...

require(['./js/common'], function (common) {
    require(['app/main-books']);
});

Hope that helps!

like image 21
jcreamer898 Avatar answered Sep 27 '22 17:09

jcreamer898


when using require.js in production it's recommended to run r.js http://requirejs.org/docs/optimization.html

What it does is: - Combines related scripts together into build layers and minifies them via UglifyJS (the default) or Closure Compiler (an option when using Java). - Optimizes CSS by inlining CSS files referenced by @import and removing comments.

That means ALL your scripts will be concatenated into one file and there won't be any extra http requests.

like image 44
alexndm Avatar answered Sep 27 '22 17:09

alexndm