Multiple scripts with requirejs - javascript

I have a problem with requirejs. Maybe I don't really get how it should be working, but for me it seems quite counterproductive that requirejs does not allow me to split up my code into different independent scripts.
Since I am using Play and its template language to build up the structure of the page, I tried to insert different javascript logic into different parts of the page component-wise. For example: I have a main.scala.html which contains components that every page needs, alltogether with their js logic. When another page needs a navigation bar, I insert this together with the corresponding logic. So I have a main.js and a navigation.js. Since they are only dependent on Jquery mobile and not on each other, I wanted to load them with different tags. The second script never gets loaded so my intuition was that requirejs seems not to allow multiple data-main attributes on one page.
So my questions is: is it possible to have multiple independent scripts in one page using requirejs? And if not, why?
Thanks in advance

The idea is that you only have one data-main attribute that loads the main.js, then inside main.js you can conditionally load other scripts
if (something) {
require(["this"], function(this) { ... });
} else {
require(["that"], function(that) { ... });
}
See: http://requirejs.org/docs/start.html
Or have I misunderstood the question?

data-main is for when you have only one requirejs app one your page. If you have multiple, don't use data-main. It's quite simple, here is an example using your main.js and navigation.js
<script src='require.js'></script>
<script>
require(['main']);
require(['navigation']);
</script>
Although I would argue your page does need navigation.js. Treat each page as an app. So data-main as normal into your main.js.
And then inside main.js:
// start independent load of navigation.js
require(['navigation']);
// load modules required for main
require(['moduleA', 'moduleB'], function(moduleA, moduleB){
// inside main.js
});

Related

Difference Between data-main and normal script loading

When using RequireJS, what's the difference between including your script with
<script data-main="scripts/main" src="scripts/require.js"></script>
and
<script src="scripts/require.js"></script>
i.e. what does the data-main attribute change about loading in a script? I've read through the docs on this, and the different isn't entirely clear to me.
You will typically use a data-main script to set configuration options and then load the first application module. Note: the script tag require.js generates for your data-main module includes the async attribute. This means that you cannot assume that the load and execution of your data-main script will finish prior to other scripts referenced later in the same page.
The documentation mentions that you'll typically use a data-main script to set configuration options and load the first application module — but can't you also do that via a plain old script tag? Is there a benefit to doing configuration loading the application module with a data-main attribute?
Is the only different with data-main the asynchronous loading? Or is there something more?
data-main is just another way to perform the initial require call of your application. To illustrate... this:
<script data-main="scripts/main" src="scripts/require.js"></script>
is equivalent to this:
<script src="scripts/require.js"></script>
<script>require(["scripts/main"])</script>
The two forms are both asynchronous. This is really all there is to it. Considerations about how many entry points you have or where the RequireJS configuration is going to be located are completely orthogonal to the use of data-main. To put it differently, these considerations play a role in your use of data-main to the exact same extent that they play a role in your use of require(["scripts/main"]).
The part of the documentation you quoted is just obscuring things by mentioning that the script loaded with data-main creates a script element in the head element with the async attribute set, because this is not different from loading any script through RequireJS. Every single script loaded by RequireJS will have a script element created for it, in the head, and have the async attribute set.
It is common to use data-main for applications that have only a single entry point, and to put RequireJS' configuration in the module specified in data-main, but it is not required by any means. For instance, this is a perfectly valid use:
<script>
require = {
// RequireJS config here...
};
</script>
<script data-main="scripts/main" src="scripts/require.js"></script>
<script>
require(["foo"], function (foo) {
foo.something();
});
</script>
The configuration is given to RequireJS by setting require in the global space before loading RequireJS. (If require is defined before RequireJS is loaded, it will take require's value as its configuration.) Besides kicking off the application by loading scripts/main, this code also loads foo and calls a method on it: two entry points.
data-main is for when you want to have a single entry point to your application. That single script line will load RequireJS along with scripts/main.js and kick off your app.
The result of
<script data-main="scripts/main" src="scripts/require.js"></script>
is that <script async src="scripts/main.js"></script> is appended to the document at runtime; this is the script that will contain your require.config() block and pull in your first application script. If you don't specify a data-main, then you're only loading Require and none of your app scripts, unless you explicitly load a config file and the first module.
What do you think Require will load if you don't tell it to load anything?
If you do not use data-main, you must provide an entry point after loading Require (this is how I have always done it, for no good reason other than that's how I learned it.) Read about the configuration options to see how you would do this.
I use this pattern in development:
<script src="js/lib/require.js"></script>
<script src="js/config.js"></script>
<script>
// same as data-main
require.config({baseUrl : 'js'});
require(['js/main']);
</script>
As a single entry point, the contents of config.js and the subsequent require(['js/main']) call would be in whichever script is referenced as data-main.
If you use the static optimizer to build a production bundle, none of this matters because you just load the bundle.
data-main is the script that require.js will process. As the documentation says, it is common to set configuration options in that script. But there are other ways to do that. In many cases it is the easiest and most effective place. But not always.
The script pointed to by data-main will also list the dependencies for the code that the file defines. The dependencies are where the meat is. It is typical, though not required, to have this first module load and execute what is ultimately the actual application.
Addendum in response to the comment:
There are some concepts you need to be aware of that will help make sense of this approach.
First is that there isn't a single (or a couple, or even a few) script(s). This type of loader is designed to handle lots and lots of very small scripts. Each with a very specific and often (preferably) single purpose. Call these scripts modules (or units).
Any given module might depend on any number of other modules in order to function. The AMD pattern allows the dependencies for each module to be listed within that module's definition.
RequireJS will sort out who needs what and in what order and not let scripts execute until all the modules they depend on are loaded and ready.
So this is not at all like putting a script link (or multiple links) in a page as we all grew up doing. It is a much different approach to javascript development. Once you get your head around it and figure out how to breakdown your code into discreet units of functionality it really is quite slick.
<script data-main="scripts/main.js" src="scripts/vendor/requirejs/require.js"></script>
src will load "scripts/vendor/requirejs/require.js" first. Then data-main attribute will execute "scripts/main.js".

How to handle dependencies when libraries are loaded asynchronously?

Google Pagespeed said I should load my JS files asynchronously, but this has introduced a problem for many of my pages with code using libraries and plugins.
For example, I have the following code on one page:
$(document).ready(function () {
var hound = new Bloodhound({ .......
});
So when the page loads, I am creating a Twitter Bloodhound (goes with Typeahead) object. The problem is, if Bloodhound and Typeahead are loaded asynchronously, then an error is thrown:
Uncaught ReferenceError: Bloodhound is not defined
This is because those scripts haven't been loaded yet.
I came up with this solution:
$(document).ready(function () {
createBloodhound();
});
function createBloodhound() {
if (typeof Bloodhound != "undefined") { // if bloodhound JS has loaded
var hound = new Bloodhound({ .......
}
else {
setTimeout(function(){
createBloodhound();
}, 10);
}
}
Is this a good practice, or is there a better way?
NOTE: I realize there are libraries like RequireJS out there to handle dependencies when loading files, but I don't think this type of solution will not work in my case because I load the libraries asynchronously in a wrapper file (since they're required for every page). The example code here would not be on every page, but only on a specific page on my website.
The best approach is to use a callback mechanism, which you can react to, rather than using a polling mechanism. I used script.js, which is simple and yet functional, and offers the callback mechanism.
Without that, you could implement something yourself. Performance-wise though, utilizing callback are better.
Depending on the complexity of your site, different options might be best. If...
All of your javascript is in JS files
Your above-the-fold content looks identical before and after the JS is loaded (or close enough to identical that the flash of change when your JS does load wouldn't distract your users)
The total file size is small (or most of your JS is needed on pages everyone will visit every time they visit your site)
... then combine them into one file and just server that instead of all the individual ones. Then you don't have to worry about dependencies at all. Include that script file at the bottom of your body tag (no need for async or defer attributes, but you can use them if you want).
If some of your javascript is necessary to make your above-the-fold content look correct, do the same thing, except split your JS into two files. One file contains only what is necessary to make the above-the-fold content look correct, and the other file contains everything else. Include the first one in your head tag (possibly inlining it), and include the second one at the bottom of your body tag. If the second one depends on the first tag, do not use the async attribute, because it might get executed first.
If you have some large JS files that are only used on some pages, and those files depend on other JS files, stick your scripts at the bottom of your body tag and use the defer attribute.
If you have javascript mixed in with your HTML, you can use a callback mechanism (like script.js), or you can build up execution queues like Google Analytics does, which the external script knows to look for when it first loads.

How/when/where to include external javascript

I'm looking for some advice on the best way to hold my JavaScript (jQuery) functions.
I am developing in MVC/razor and therefore have a layout page. I include my jQuery library and an external JavaScript file in here so it's available in every single page.
This is working well, but I am now becoming very aware of the fact that I am adding almost 300 lines of JS to EVERY page, where maybe half of that is used in any one of these pages.
One function is not in the external file and instead sits inside the HTML because I need to use variables set in my razor code.
I have a couple of questions around this arrangement:
Is placing JS inside the HTML generally acceptable when variables set using razor are used? There does not appear to be a clean way of passing a variable into an external js file
Should I split my functions down in to individual JS files and just include what is needed for each page in the site?
If I were to split them into multiple files, how would that work with jQuery's (document).ready ? Do I need to use that if all the JavaScript I am including is to be used?
I'm sure this will more a matter of opinion than a black and white answer, but I want to consider all my options before moving on. Even though it works fine as is, I can't help but feel there is a better/cleaner way.
Remember once a user lands on your homepage and loads the javascript file it will be cached in their browser so subsequent pages will not download the Javascript again.
I would definitely keep the js separate, you could have a snippet on each page that initialise the JS that that particurlar view needs. Put something like the below in the views that need to run JS
$(document).ready(function() {
mysite.mypage();
});
Then the function mysite.mypage() can be defined in the external JS file.
300 lines isnt the end of the world, I would say its probably too early to be worryign about optimisation.
You could always look at minifying that JS file to decrease the size. A quick and easy way to do this is here:
http://www.minifyjavascript.com/
Have you ever heard of require.js? http://requirejs.org/ I find it really useful.
It's a module loader so you are able to split all of your JS code into individual files and load only the ones you need on each page.
I don't know about passing a variable to an external JS file, I don't think its possible / the 'right' way.
You can make each external JS file into a function that accepts and returns parameters. Then in the page you need to use it:
- include the file dependancy
- call the function
Thats what I do, seems like your 2nd suggestion.
for the $(document.ready) question its really up to you. You don't have to use it but its useful for some things , check out this overview:
http://docs.jquery.com/Tutorials:Introducing_$(document).ready()

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

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