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".
Related
I've been thinking around this question lot of days and i have decided to ask the experts.
How browsers will handle the new import/export syntax ? I mean: will the modules be loaded asynchronously ? Referencing only my main or entry file and browsers will lazy load the requiere modules.
Maybe am i missing or misunderstanding something about this new architecture ?
Thank you very much!
Regards.
This is standardized now and supported by all major modern browsers.
will the modules be loaded asynchronously?
Yes, with two options available; details below.
Referencing only my main or entry file and browsers will lazy load the requiere modules.
Not so much "lazy," but yes.
Enabling it
Details in the spec here and here (and possibly elsewhere).
To get this behavior, you specify that your script is a module by using type="module":
<script src="main.js" type="module"></script>
or for inline scripts
<script type="module">
// ...module code here
</script>
That means that the script is parsed and handled per the Module definition in the JavaScript specification instead of per the Script definition, which means it can have imports (and exports).
Imports are resolved relative to the script's URL (for modules loaded via a separate resource such as the main.js above, just like CSS) or relative to the document (for inline modules like the one above).
So for instance, if I have this in my document at http://example.com/index.html:
<script src="./handy/stuff/nifty.js" type="module"></script>
...and nifty.js contains
import Thingy from "./thingy.js";
...then the browser looks for http://example.com/handy/stuff/thingy.js, not http://example.com/thingy.js. Again, just like CSS imports.
Note that the ./ on that module specifier is required, just from "thingy.js" won't work. That's because bare specifiers are disallowed because they'll probably end up having a special meaning. (For instance, in Node.js, that's how you specify built-in modules, and modules installed in node_modules.) A module specifier must be a full URL, or a relative URL starting with /, ./, or ../.
Async
I said above that modules are loaded asynchronously, and there are two options available. This graphic from the spec says it best (see the spec for the latest copy of it):
As you can see, for type="module" scripts, if you don't put any special flag attributes on the script tag, all of the module's dependencies will be resolved and then the script will be run once parsing of the HTML is complete. If you include the async attribute, it may run sooner, before the HTML parsing is complete (for instance, if all the scripts are in cache). (defer is not valid for modules.)
According to this post in Mozilla's website, it's up to the implementation:
Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at the import declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network!
This may change in the future, as it is still not fully standardized, but you can be sure that you will not need to add a script tag for every module.
Some module loaders today bundle all the files for you, but that's may not be the case when the future will be live, as it will not have an advantage in performance in HTTP2.
You can read the ES6 specification of import here.
I've been thinking around this question lot of days and i have decided to ask the experts.
How browsers will handle the new import/export syntax ? I mean: will the modules be loaded asynchronously ? Referencing only my main or entry file and browsers will lazy load the requiere modules.
Maybe am i missing or misunderstanding something about this new architecture ?
Thank you very much!
Regards.
This is standardized now and supported by all major modern browsers.
will the modules be loaded asynchronously?
Yes, with two options available; details below.
Referencing only my main or entry file and browsers will lazy load the requiere modules.
Not so much "lazy," but yes.
Enabling it
Details in the spec here and here (and possibly elsewhere).
To get this behavior, you specify that your script is a module by using type="module":
<script src="main.js" type="module"></script>
or for inline scripts
<script type="module">
// ...module code here
</script>
That means that the script is parsed and handled per the Module definition in the JavaScript specification instead of per the Script definition, which means it can have imports (and exports).
Imports are resolved relative to the script's URL (for modules loaded via a separate resource such as the main.js above, just like CSS) or relative to the document (for inline modules like the one above).
So for instance, if I have this in my document at http://example.com/index.html:
<script src="./handy/stuff/nifty.js" type="module"></script>
...and nifty.js contains
import Thingy from "./thingy.js";
...then the browser looks for http://example.com/handy/stuff/thingy.js, not http://example.com/thingy.js. Again, just like CSS imports.
Note that the ./ on that module specifier is required, just from "thingy.js" won't work. That's because bare specifiers are disallowed because they'll probably end up having a special meaning. (For instance, in Node.js, that's how you specify built-in modules, and modules installed in node_modules.) A module specifier must be a full URL, or a relative URL starting with /, ./, or ../.
Async
I said above that modules are loaded asynchronously, and there are two options available. This graphic from the spec says it best (see the spec for the latest copy of it):
As you can see, for type="module" scripts, if you don't put any special flag attributes on the script tag, all of the module's dependencies will be resolved and then the script will be run once parsing of the HTML is complete. If you include the async attribute, it may run sooner, before the HTML parsing is complete (for instance, if all the scripts are in cache). (defer is not valid for modules.)
According to this post in Mozilla's website, it's up to the implementation:
Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at the import declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network!
This may change in the future, as it is still not fully standardized, but you can be sure that you will not need to add a script tag for every module.
Some module loaders today bundle all the files for you, but that's may not be the case when the future will be live, as it will not have an advantage in performance in HTTP2.
You can read the ES6 specification of import here.
I'm trying out AMD-way of handling scipts and my choise fell upon requirejs. In this project I use MDL (front-end framework; for those who haven't heard of it think of it as bootstrap 3) which should be included as:
<link rel="stylesheet" href="/bower_components/material-design-lite/material.min.css">
<script src="/bower_components/material-design-lite/material.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
I am not interested in js API that this framework is providing (if it provides any), I need this script only for UI to work properly when I attach framwerk-specific classes to elements.
According to requirejs philosophy I need only one script file to be included with a script tag on my page - the entry point. I understand that in that main script I need to require dependencies. And if it was say jQuery or underscore i.e. the library I actually require and my code depends on, I would write something like:
require(
['jquery'],
function($) {
$('body').append(...);
}
);
But how do I roll if it's not an actual dependency but I still need it to be loaded in my page and, in this particular case, I need it to be loaded first.
What do I do? My guess is I remove the script tag from my head and specify it in square brackets in my entry point script (as I did for jquery in snippet above) but just don't use it. Is it correct?
But how do I roll if it's not an actual dependency but I still need it to be loaded in my page and, in this particular case, I need it to be loaded first.
RequireJS only guarantees the relative order in which modules are loaded. If a module must absolutely be loaded first, then the chain of dependencies must be such that everything else depends on it, directly or indirectly. RequireJS does not provide a method to say "load this before everything else". You can get this effect only through dependencies. (Sometimes people think the deps configuration option guarantees that some modules will load first. It does not. Or they think that the order of dependencies in a require or define call sets an order for loading beyond what the dependencies set. It does not. If A and B have no dependencies of their own then require(['A', 'B'], ... and require['B', 'A'], ... are both free to load the modules in any order.)
What do I do? My guess is I remove the script tag from my head and specify it in square brackets in my entry point script (as I did for jquery in snippet above) but just don't use it. Is it correct?
In theory there's no problem with doing this. I say "in theory" because I do not use MDL so I don't know if MDL has any need that would prevent it from working. You've compared MDL with Bootstrap 3. What you describe is how I load Bootstrap with RequireJS. I tend to use the CommonJS idiom so modules that need Bootstrap look something like this:
define(function (require, exports, module) {
'use strict';
require("bootstrap");
There's no need to do something like var bootstrap = require("bootstrap"); because the value would be undefined anyway because the JS code of Bootstrap installs itself as jQuery plugins.
I my webapp I use about 30 js files, each file containing 1 function. All these function are now selfinvoking and have references to each other.
The problem with this is that the order of scripts in the index.jsp matters. If a method is called on a function which has not been invoked yet we get a undefined error.
For a while we could overcome this by controlling the order of the <script> tags, but I would like to do this by using a loader script.
I have set up a small fiddle to show my concept. My biggest concern is that I have to declare my objects globally, in order to have them be accessible in the jquery(document).ready() function.
Is this an OK pattern? Any hints highly appreciated!
You could use RequireJS or similar loader, which would handle script dependencies for you.
You would need to modify each of JS file to make it a module in a similar fashion to this example:
// File: module3.js
define(["module1", "module2"], function(m1, m2) {
// Here, module1 and module2 are guaranteed to be loaded.
});
Then, you would make one "main" script (I usually call it main.js) and require several modules:
require(["module3"], function (m3) {
// Here module3 is loaded, as well as module1 and module2
// - because module3 depends on them.
});
And put this in your HTML:
<script data-main="scripts/main" src="scripts/require.js"></script>
Try to build your server-side architecture to serve proper js files (and other static files) per page. Create 1 minified js file for page and initialize objects scope in html files.
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
});