RequireJS async loading leads to poor user experience (UX)? - javascript

I'm using RequireJS to help me manage complex relationships/dependencies between some of my homegrown Javascript modules. It works very well for that -- loads them in the correct order based on their dependencies.
I'm also using RequireJS to load known libraries such as jQuery and KnockoutJS.
This being said, my issue is this -- let's say I have a simple login form page. It uses jQuery to enable some interaction (example: validating input, etc.). As such, I use RequireJS to include jQuery in my page's Javascript code. But, since RequireJS require() calls are asynchronous, there's a potential 'delay' between the moment the page is shown to the user, and the moment the jQuery library is loaded and kicks in.
So here's my problem: in the hypothetical scenario where the jQuery library takes a while to load, I want to prevent the user from being able to manipulate/submit the form until jQuery has kicked in. So, at the moment, the login form is initially hidden (displays a 'Loading...' message), and at the end of my require() async callback, once jQuery is loaded and applied, I make the form visible.
I find that this leads to a somewhat poor user experience -- you load the page, it's missing stuff at first (showing 'Loading...'), and then the form appears. In most cases it loads pretty quickly, so the page looks like it 'blinks' as it goes from the 'Loading...' phase to showing the full form almost instantly.
I've been thinking of moving the big libraries (jQuery, KnockoutJS) outside of RequireJS for that reason.
Is this normal or expected? Am I approaching this wrong?
TL;DR version: since RequireJS's require() mechanism is asynchronous... if your page needs some modules to work properly, do you hide the page's contents until the modules are loaded, and then make the page's contents visible in the end? Would this be considered poor UX?

Ah you probably aren't optomising your requirejs assets,
i use gruntjs to compile all my requirejs assets into one big js file,
if you leave requirejs unoptimized yes it can all be a bit slow, for dev it's great to easily have everything link dynamically, for production it's normally best to compile everything together into one big file for download speed (every file adds about 100ms and 1.3k onto your page load)
should mention for dynamic javascript perhaps created by php or something, you can ignore them from the concat process using empty:, and then add a query string which will make you able to using a .php file end ending, but your normally better writing static code which uses a dynamic json feed loaded by the static code
Some links about the requirejs optomiser, ps you can do this your self by concating all the files together in a bat/sh script yourself
http://requirejs.org/docs/optimization.html
you will probaly need to repeat your main.js javascript lookup rules in your grunt file, if you have any special library locations or shims in place
example grunt file i use currently
module.exports = function(grunt) {
// Project configuration.
...
requirejs: {
production: {
options: {
// REMEMBER TO DUPLICATE CHANGES IN MAIN.JS, example dynamic javascript created by ajax, and static javascript in library folder
paths: {
"moment": "../shared/js/moment/2.5.0/moment.min",
"dynamic.ottconfig": "empty:"
},
shim: {
"lib.filesaver": {deps: ["shim.blob"]},
...
},
name: "main", // link to almond.js or requirejs.js
appUrl: "./web/tmp/js",
baseUrl: "./web/tmp/js",
out: "web/bin/js/main.min.js",
optimize: "uglify2",
preserveLicenseComments: false,
generateSourceMaps: true,
insertRequire: [ "main" ]
}
}
},
...);
grunt.loadNpmTasks('grunt-contrib-requirejs');
// Default Production Build task(s).
grunt.registerTask('default', [
...
'requirejs',
...
]);
};

Related

Dojo build css and custom javascript

I've set up a single html page that uses three dojo widgets and I'm trying to create a custom build from it using dojo 1.7.5. The build succeeds leaving me with a dojo.js that includes the files I need using this build file:
var dependencies = {
action: "release",
selectorEngine: "acme",
stripConsole: "none",
cssOptimize: "comments.keepLines",
layers: [
{
name: "dojo.js",
dependencies: [
"dijit.form.ValidationTextBox",
"dijit.form.DropDownButton",
"dijit.form.Button",
"dijit.form.Form",
"dijit._base",
"dijit._Container",
"dijit._HasDropDown",
"dijit.form.ComboButton",
"dijit.form.ToggleButton",
"dijit.form._ToggleButtonMixin",
"dojo.parser",
"dojo.date.stamp",
"dojo._firebug.firebug"
]
}, {
name: "../test/test.js",
dependencies: [
"test.test"
]
}
],
prefixes: [
[ "dijit", "../dijit" ],
[ "dojox", "../dojox" ],
[ "ourpeople", "../ourpeople" ]
]
};
The questions I can't seem to find an answer to:
I'm using cssOptimize, I was expecting a single css file in which all the used css files were imported. However I can't find such a file. Is this the way dojo compresses it's css or are my expectations wrong? If so where can I find it in my release folder?
My test.js contains a function test1() if I call it from my built js it states test1 is not defined. I call that function directly without dojo. I'm assuming that building custom js only works if it is a dojo class using declare?
Final question, I needed to include several dojo files in the build manually such as dojo._firebug.firebug since after my initial build it was still using xhr calls to get those files. After including the files manually I still see xhr calls from dojo to specific resources: dojo/nls/dojo_ROOT and dijit/form/nls/validate.js. Those files are created during the build process and therefore can't be included in the dependencies in the build profile. Anyone any thoughts on this matter since I'm looking to distribute dojo in a single file.
I'm fairly new to the dojo build system and (especially) so perhaps I'm expecting things that the dojo build system isn't designed to do or maybe om going about this the wrong way if so any tips or suggestions are more than welcome.
Cheers!
Test.js:
function test1() {
console.log("test1");
}
Index.php:
<script type="text/javascript" src="js/release/dojo/dojo/dojo.js"></script>
<script type="text/javascript" src="js/release/dojo/test/test.js"></script>
<script type="text/javascript">
dojo.require("dijit.form.ValidationTextBox");
dojo.require("dijit.form.Button");
dojo.require("dijit.form.Form");
dojo.ready(function() {
test1();
});
</script>
I'm using cssOptimize, I was expecting a single css file in which all the used css files were imported. However I can't find such a file. Is this the way dojo compresses it's css or are my expectations wrong? If so where can I find it in my release folder?
When you use cssOptimize, the Dojo build optimizes and flattens CSS files in place. So for example, if you're using Dijit's Claro theme, when you load dijit/themes/claro/claro.css from source, it contains a series of #import statements which in turn load more files. When you load claro.css from a build with cssOptimize, it is one file containing all of the styles previously referenced via those separate files.
My test.js contains a function test1() if I call it from my built js it states test1 is not defined. I call that function directly without dojo. I'm assuming that building custom js only works if it is a dojo class using declare?
Dojo doesn't expect every JS file to be a "class" using declare but it does expect each file to be a module which doesn't implicitly define globals (since globals should be avoided in modules anyway). When the build process encounters a module that it thinks or knows isn't AMD, it assumes it's a legacy Dojo module and wraps it in a boilerplate to convert it to AMD. This boilerplate ends up encapsulating your globals into a function scope, so they are no longer globals.
Given that you're using Dojo 1.7, you should ideally be using the AMD format to define and consume modules. dojotoolkit.org has a tutorial introducing AMD modules, and if you're migrating from Dojo 1.6 or earlier, there's also a tutorial to help you transition.
Final question, I needed to include several dojo files in the build manually such as dojo._firebug.firebug since after my initial build it was still using xhr calls to get those files. After including the files manually I still see xhr calls from dojo to specific resources: dojo/nls/dojo_ROOT and dijit/form/nls/validate.js. Those files are created during the build process and therefore can't be included in the dependencies in the build profile. Anyone any thoughts on this matter since I'm looking to distribute dojo in a single file.
I'm not sure why you're seeing dojo/_firebug/firebug being automatically loaded, but based on what you've said/shown above I would immediately suggest the following:
Convert your modules/code to AMD format
Add async: true to your dojoConfig which will cause the loader to operate in asynchronous mode, which means:
It loads modules through script injection instead of synchronous XHR
It won't unconditionally load all of dojo/_base
Add customBase: true to your dojo/dojo layer which will prevent the build from defaulting to include all of dojo/_base
As for the nls modules, to an extent it's normal to still see NLS files requested, though if your build is configured properly there would ordinarily just be one NLS file per layer and that's it (the fact that you're seeing a separate request for validate leads me to think you haven't covered all of your dependencies). The reason NLS remains separate is because there is one NLS bundle per locale, and it doesn't make sense to build all locales into one layer - that would force people to pay for resources in 20 languages they don't care about.

Using multiple pre-compiled templates with Handlebars .js (multiple HTTP requests)?

I think this question will give mine a little more context:
Using pre-compiled templates with Handlebars.js (jQuery Mobile environment)
Basically I'm trying to learn the precompiling stuff, so I can save load time and keep my html documents neat. I haven't started it yet, but based on the above link, every template needs to have it's own file. Isn't that going to be a lot of links to load? I don't want to be making multiple HTTP requests if I don't have to.
So if someone could shed some light, perhaps offer an alternative where I can get the templates out of my html, but not have to load up 100 different template files.
Tools like Grunt.js allow you to have your templates and consume them too. For example, this file compiles the templates and then concatenates them into a single file:
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-contrib-handlebars");
// Project configuration.
grunt.initConfig({
// Project metadata, used by the <banner> directive.
meta: {},
handlebars: {
dist: {
options: {
namespace: "JST",
wrapped: "true"
},
files: {
"templates.js": [
"./fileA.tmpl",
"./fileB.tmpl"
]
}
}
}
});
// Default task.
grunt.registerTask("default", "handlebars");
};
What I've yet to work out since I'm just getting started with pre-compiled templates is the workflow. I want to be able to have compiled templates when we're running a deployed version of the app but when doing development and debugging I'd much rather have my original individual files in uncompiled form so I can just edit them and reload the page.
Follow Up:
I wanted to come back to this after having worked out some of how to both have my pre-compiled templates when available and use individual templates which can be edited on the fly when people are doing development and debugging work and would like to have quick edit-reload-test cycles without doing Grunt builds.
The answer I came up with was to check for the existence of the JST[] data structure and then further to test and see whether a particular pre-compiled template is present in that structure. If it is then nothing further need be done. If it's not there then the template is loaded (we use RequireJS to do that) and compiled and put into the same JST[] array under the same name it would have if pre-compiled templates had been loaded.
That way when it comes time to actually use the template, the code only looks for it in one place and it's always the same.
In the near future I think we'll likely have RequireJS plugins to perform the test and load/compile code while keeping it simple for developers.

How to load a specific code regarding the current page?

I'm building a complex web app and to keep things simple, I made various modules (objects) in various files. Those modules can be required on some page, and not on others.
For that reason, I'd like to avoid loading all the modules for any pages, increasing the amount of useless requests.
So far, I work like this :
I include all needed libraries
After that, I instantiate these librairies in jQuery(function() {}); with specifics #ids or .classes arguments for that current page
Everything works fine, but since my app is growing beyond easy, I'd like to manage my JS with RequireJS.
And that's where things start to be a little confusing for me.
I know I can load my module when required, using require(['module1', 'module2', 'etc'], function (module1, module2, etc) {}), but how can I say :
"on this page, you load these modules, and instantiate them with those #ids and .classes"
and
"on this other page, you load only that module, with this #other-id"
?
module1 will, for example, load data from the api, and list them to a specific table given as parameter :
// in page 1
var mod1 = new Module1($('#content>table');
mod1.init(); // will load the data from the api with the url located at <table data-api-url="http://....">
// in page 2
var mod1 = new Module1($('#content .items>table'); // The table where we want the data to be populated is not at the same position!
mod1.init();
That means, depending on the page, I'll have to load my modules differently. That's how I don't know how to do using RequireJs :/
What you need is a javascript file for each page. That file will be responsible to execute your page-specific code.
I'll assume that you will use r.js to optmize and pack your code.
We can interpret Module1, Module2 etc. as libraries, because they will be used on multiple pages. To avoid browser do one request for each library module you can include this modules on your optimized main file:
Configure the "modules:" attribute of your build profile like this:
...
modules: [
{
name: "main" // this is your main file
includes: [
"module1",
"module2",
"etc..."
]
}
]
...
By doing this you tell to requirejs something like this: Optimize my "main.js" file and include in it all its dependencies, also includes the "module1", "module2" etc.
You have to do this because on your main file you do not include these modules on the require()/define() call, but you still want they available in case the page-specific module needs them. You don't have to do this for every library module you have, just for those that will be used by most of your pages.
Then, you create a javascript file for your page to use those modules:
define(function(require, exports, module) {
var $ = require("jquery"),
module1 = require("module1");
var mod1 = new Module1($('#content>table'));
mod1.init();
//other page specific-code.
});
And then on the html file:
<script data-main="main" data-start="home" src="require.js"></script>
So, when page loads, it will make a request for require.js, then another for main.js and then another for home.js and that's all.
I'll put the link for the another question so this answer get some context: How to use RequireJS build profile + r.js in a multi-page project
The creator of RequireJS actually made an example project that uses page-specific main files: https://github.com/requirejs/example-multipage

Using the optimizer in requirejs to group and load files

We have just started using requirejs in our project and we are loving
it. Currently we are just using it to manage our many jQuery plugins
the site runs on, so are barely scratching the surface of its
potentialā€¦ but due to time constraints this has to wait.
I'm having some problems getting the optimizer play as I'd like it to,
I wonder if you could tell me if it is even possible to do this.
My directory structure:
> /common/
> /common/*.js - common plugins used on most pages on the site
> /infrequent/
> /infrequent/infrequentModule.js
> /infrequent/ckeditor
> /infrequent/ckeditor/1000s.js
> /require.js
> /jquery.js
> /main.js - this file contains all scripts in common. It looks at the page and call the necessary plugins.
Optimization/build hopes:
main.js would contain all the common scripts. On most pages this script will be the only one loaded (except require.js)
main.js looks at the page, if it see's that it needs an infrequent plugin/module it loads that async. So on a infrequent page I may load require.js, main.js and infrequentModule.js.
I have tried various methods in the build script to exclude and
include files to get the desired results but to no avail.
Does anyone have any ideas or know if this is, in fact, even possible?
OK, so I got some great answers on the google group for require.js - http://groups.google.com/group/requirejs/browse_thread/thread/359b02473f4e707
The following is a useful answer I was given that I hope may help out someone elseā€¦
Hi, I am currently doing this in one of my projects, here is my
build.js:
paths: {
//load libs from cdn
"jqueryUI": "jqueryUI", //temp static file for build
"swfobject": "swfobject", //temp static file for build
"Templates": "../Templates/Settings"
},
modules: [{
name: "appName",
excludeShallow: ["jqueryUI", "swfobject", "Modules/
module.preferences"]
}
Then in one of my modules I have a require call for Modules/
module.preferences which I execute when I need it:
function loadPreferences(){
require(["Modules/module.preferences"],function(){
//Execute some init code after the module has
loaded
})
}
}
This achieves what you described where I have require and main loaded
on page load, then module.preferences loads async when I need it.
Hope that helps, Jeff.

What's the difference between Require.js and simply creating a <script> element in the DOM? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
What's the difference between using Require.JS amd simply creating a <script> element in the DOM?
My understanding of Require.JS is that it offers the ability to load dependencies, but can this not simply be done by creating a <script> element that loads the necessary external JS file?
For example, lets assume I have the function doStuff(), which requires the function needMe(). doStuff() is in the external file do_stuff.js, while needMe() is in the external file need_me.js.
Doing this the Require.JS way:
define(['need_me'],function(){
function doStuff(){
//do some stuff
needMe();
//do some more stuff
}
});
Doing this by simply creating a script element:
function doStuff(){
var scriptElement = document.createElement('script');
scriptElement.src = 'need_me.js';
scriptElement.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(scriptElement);
//do some stuff
needMe();
//do some more stuff
}
Both of these work. However, the second version doesn't require me to load all of the Require.js library. I don't really see any functional difference...
What advantages does Require.JS offer in comparison to simply creating a element in the DOM?
In your example, you're creating the script tag asynchronously, which means your needMe() function would be invoked before the need_me.js file finishes loading. This results in uncaught exceptions where your function is not defined.
Instead, to make what you're suggesting actually work, you'd need to do something like this:
function doStuff(){
var scriptElement = document.createElement('script');
scriptElement.src = 'need_me.js';
scriptElement.type = 'text/javascript';
scriptElement.addEventListener("load",
function() {
console.log("script loaded - now it's safe to use it!");
// do some stuff
needMe();
//do some more stuff
}, false);
document.getElementsByTagName('head')[0].appendChild(scriptElement);
}
Arguably, it may or may not be best to use a package manager such as RequireJS or to utilize a pure-JavaScript strategy as demonstrated above. While your Web application may load faster, invoking functionality and features on the site would be slower since it would involve waiting for resources to load before that action could be performed.
If a Web application is built as a single-page app, then consider that people won't actually be reloading the page very often. In these cases, preloading everything would help make the experience seem faster when actually using the app. In these cases, you're right, one can merely load all resources simply by including the script tags in the head or body of the page.
However, if building a website or a Web application that follows the more traditional model where one transitions from page to page, causing resources to be reloaded, a lazy-loading approach may help speed up these transitions.
Here is the nice article on ajaxian.com as to why use it:
RequireJS: Asynchronous JavaScript loading
some sort of #include/import/require
ability to load nested dependencies
ease of use for developer but then backed by an optimization tool that helps deployment
Some other very pointed reasons why using RequireJS makes sense:
Managing your own dependencies rapidly falls apart for sizable projects.
You can have as many small files as you want, and don't have to worry about keeping track of dependencies or load order.
RequireJS makes it possible to write an entire, modular app without touching window object.
Taken from rmurphey's comments here in this Gist.
Layers of abstraction can be a nightmare to learn and adjust to, but when it serves a purpose and does it well, it just makes sense.
Here's a more concrete example.
I'm working in a project with 60 files. We have 2 different modes of running it.
Load a concatenated version, 1 large file. (Production)
Load all 60 files (development)
We're using a loader so we just have one script in the webpage
<script src="loader.js"></script>
That defaults to mode#1 (loading the one large concatenated file). To run the in mode#2 (separate files) we set some flag. It could be anything. A key in the query string. In this example we just do this
<script>useDebugVersion = true;</script>
<script src="loader.js"></script>
loader.js looks something like this
if (useDebugVersion) {
injectScript("app.js");
injectScript("somelib.js");
injectScript("someotherlib.js");
injectScript("anotherlib.js");
... repeat for 60 files ...
} else {
injectScript("large-concatinated.js");
}
The build script is just an .sh file that looks like this
cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js
etc...
If a new file is added we'll likely be using mode#2 since we're doing development we have to add an injectScript("somenewfile.js") line to loader.js
Then later for production we also have to add somenewfile.js to our build script. A step we often forget and then get error messages.
By switching to AMD we don't have to edit 2 files. The problem of keeping loader.js and the build script in sync goes away. Using r.js or webpack it can just read the code to build large-concantinated.js
It can also deal with dependencies, for example we had 2 files lib1.js and lib2.js loaded like this
injectScript("lib1.js");
injectScript("lib2.js");
lib2 needs lib1. It has code inside that does something like
lib1Api.installPlugin(...);
But as the injected scripts are loaded asynchronously there's no guarantee they'll load in the correct order. These 2 scripts are not AMD scripts but using require.js we can tell it their dependencies
require.config({
paths: {
lib1: './path/to/lib1',
lib2: './path/to/lib2',
},
shim: {
lib1: {
"exports": 'lib1Api',
},
lib2: {
"deps": ["lib1"],
},
}
});
I our module that uses lib1 we do this
define(['lib1'], function(lib1Api) {
lib1Api.doSomething(...);
});
Now require.js will inject the scripts for us and it won't inject lib2 until lib1 has be loaded since we told it lib2 depends on lib1. It also won't start our module that use lib1 until both lib2 and lib1 have loaded.
This makes development nice (no build step, no worrying about loading order) and it makes production nice (no need to update a build script for each script added).
As an added bonus we can use webpack's babel plugin to run babel over the code for older browsers and again we don't have to maintain that build script either.
Note that if Chrome (our browser of choice) started supporting import for real we'd probably switch to that for development but that wouldn't really change anything. We could still use webpack to make a concatenated file and we could use it run babel over the code for all browsers.
All of this is gained by not using script tags and using AMD

Categories

Resources