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 )
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:
Any ideas would be nice.
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.
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.
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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With