Today’s websites are evolving into web apps:
As a result there is a lot of code on the client side!
A big code base needs to be organized. Module systems offer the option to split your code base into modules.
There are multiple standards for how to define dependencies and export values:
<script>
-tag style (without a module system)<script>
-tag styleThis is the way you would handle a modularized code base if you didn’t use a module system.
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>
Modules export an interface to the global object, i. e. the window
object. Modules can access the interface of dependencies over the global object.
require
This style uses a synchronous require
method to load a dependency and return an exported interface. A module can specify exports by adding properties to the exports
object or setting the value of module.exports
.
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
It’s used on server-side by node.js.
Asynchronous Module Definition
Other module systems (for the browser) had problems with the synchronous require
(CommonJs) and introduced an asynchronous version (and a way to define modules and exporting values):
require(["module", "../file"], function(module, file) { /* ... */ });
define("mymodule", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
Read more about CommonJs and AMD.
EcmaScript6 adds some language constructs to JavaScript, which form another module system.
import "jquery";
export function doStuff() {}
module "localModule" {}
Give the developer the choice of the module style. Allow existing code to work. Make it easy to add custom module styles.
Modules should be executed on the client, so they must be transferred from the server to the browser.
There are two extremes on how to transfer modules:
Both are used in the wild, but both are suboptimal:
A more flexible transferring would be better. A compromise between the extremes is better in most cases.
→ While compiling all modules: Split the set of modules into multiple smaller batches (chunks).
We get multiple smaller requests. Chunks with modules that are not required initially are only requested on demand. The initial request doesn’t contain your complete code base and is smaller.
The “split points” are up to the developer and optional.
→ A big code base is possible!
Note: The idea is from Google’s GWT.
Read more about Code Splitting.
Why should a module system only help the developer with JavaScript? There are many other static resources that need to be handled:
And also:
This should be as easy as:
require("./style.css");
require("./style.less");
require("./template.jade");
require("./image.png");
Read more about Using loaders and Loaders.
When compiling all the modules a static analysis tries to find dependencies.
Traditionally this could only find simple stuff without expression, but i.e. require("./template/" + templateName + ".jade")
is a common construct.
Many libraries are written in different styles. Some of them are very weird…
A clever parser would allow most existing code to run. If the developer does something weird it would try to find the most compatible solution.