We're building a non-trival web application using Backbone, RequireJS and Handlebars, and well, I'm just curious. At the moment, each of our models sorta looks like this:
define(['Backbone', 'js/thing/a', 'js/thing/b', 'js/lib/bob'], function(a, b, bob) { return Backbone.Router.extend({ // stuff here }); });
where thing/a, thing/b both have their own dependencies, for example on Handlebars templates, etc. What happens now is that in my main.js, all of the 'top-level' routers are loaded and initialized; each top-level router has a set of dependencies (models, views, etc) which each have their own dependencies (templates, helpers, utils, etc). Basically, a big tree structure.
The problem in this case is that this entire tree is resolved and loaded on page load. I don't mind that per sé, as we'll run it through the optimizer eventually and end up with one big single file (reducing RequireJS to basically a modularization framework). However, I am curious whether you can load stuff like views and templates 'on demand'.
There is the "simplified CommonJS wrapping" explained here, so I tried that:
define(function(require) { Backbone = require('Backbone'); return Backbone.Router.extend({ doStuff: function() { var MyView = require('js/myView'); new MyView().render(); } }); });
However, looking at Chrome's network inspector, it seems that RequireJS - somehow, even without triggering the route that triggers the doStuff handler - still loads the myView
dependency. Questions:
require()
without actually triggering the doStuff
route?To lazy load an image, display a lightweight placeholder image, and replace with the real full-size image on scroll. There are several technical approaches to lazy loading images: Inline <img> tags, using JavaScript to populate the tag if image is in viewport. Event handlers such as scroll or resize.
Today, lazy loading is widely used in web applications to improve application performance. It helps developers reduce loading times, optimize data usage and improve the user experience. However, overusing lazy loading can affect the application performance negatively.
You should always use lazy loading for the images below the fold. As we explained, lazy loading will reduce the real and perceived loading time. User experience will benefit from it — and you users will thank you.
Lazy loading is a strategy to identify resources as non-blocking (non-critical) and load these only when needed. It's a way to shorten the length of the critical rendering path, which translates into reduced page load times.
Is this actually possible? Are there black magicks in RequireJS that looks for calls to require() without actually triggering the doStuff route?
When you use the 'sugar' syntax it uses Function.prototype.toString
and a regex to extract your references to require
and then lists them as dependencies before running the function. Basically it becomes the normal style of define with an array of deps as the first argument.
Because of this, it doesn't care where your require calls are and that's why conditional statements are ignored (it also explains why those require
calls have to use a string literal, and not a variable).
Is this the theoretically correct way of going about 'on-demand', lazy loading of RequireJS modules and resources?
Using the sugar syntax won't allow conditional loading as you've seen. The only way I can think of off the top of my head is to use a require
call with an array of deps and a callback:
define(function(require) { var module1 = require('module1'); // This will only load if the condition is true if (true) { require(['module2'], function(module2) { }); } return {}; });
Only downside is another nested function but if you're after performance then this is a valid route.
Does the r.js optimizer still work as advertised if you use this notation?
If you're using the 'sugar' syntax then yes, the optimiser will work fine. An example:
modules/test.js
define(function(require) { var $ = require('jquery'); var _ = require('underscore'); return { bla: true } });
Once compiled by r.js this looks like:
define('modules/test', ['require', 'jquery', 'underscore'], function(require) { var $ = require('jquery'); var _ = require('underscore'); return { bla: true } });
In conclusion you can load stuff conditionally, but as you mentioned, if you intend to optimise the project with r.js then there isn't a huge overhead in just using the sugar syntax.
You may also want to check out require-lazy.
It has a runtime component and a build time component. The runtime component allows you to lazily require a module as (note the lazy!
plugin):
define(["lazy!mymodule"], function(mymodule) { ... });
In the previous context, mymodule
is a promise, the real module will be loaded with get()
and will be made available in the then()
callback:
mymodule.get().then(function(m) { // here m is the real mymodule });
Require-lazy integrates with r.js to automatically create "bundles" of Javascript files. It also handles automatically cache-busting for the bundles. There are several examples to get an idea. There is also Grunt and Bower integration.
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