is an introduction to ECMAScript 6 modules and how they can be used in current browsers. In contrast, this blog post explains how future browsers will support them natively. As part of that support, we will get the <module> tag, a better version of the <script> tag.
- Load modules synchronously, while the body is executed. That is what Node.js does.
- Load all modules asynchronously, before the body is executed. That is how AMD modules are handled. It is the only option for browsers, because modules are loaded over the internet. Otherwise, execution would pause for too long. As an added benefit, this approach allows one to load multiple modules in parallel.
ECMAScript 6 modules in browsersECMAScript 6 gives you the best of both worlds: The synchronous syntax of Node.js plus the asynchronous loading of AMD. To make both possible, the ECMAScript 6 modules are syntactically less flexible than Node.js modules: Imports and exports must happen at the top level. That means that they can’t be conditional, either. This restriction allows an ECMAScript 6 module loader to analyze statically what modules are imported by a module and load them before executing its body.
Given the synchronicity of scripts, it is obvious that you can’t simply add import and export capability and turn them into modules. There must be a way to handle module code differently. Therefore, there will be a new tag <module> for modules that replaces the <script> tag and is completely asynchronous:
<module> import $ from 'lib/jquery'; var x = 123; // The current scope is not global console.log('$' in window); // false console.log('x' in window); // false // `this` still refers to the global object console.log(this === window); // true </module>As you can see, the tag has its own scope and variables “inside” it are local to that scope. Note that module code is implicitly in strict mode . This is great news – no more 'use strict';
Similar to <script>, <module> can also be used to load external modules. For example, the following tag starts a web application via a main module (the attribute name import is my invention, it isn’t yet clear what name will be used).
<module import="impl/main"><module>For legacy browsers, the following tag is a polyfillable  alternative (either with inner text or an attribute import):
url-for-package SEPARATOR path-inside-packageTo support graceful degradation, the separator must be sent to the server by current browsers, must not be rejected by current servers and must not appear in current web content. Furthermore, package URL and separator must not impede the resolution of relative paths (including those with two dots in them). That means, for example, that neither of them can include a question mark, because that limits the resolution of relative paths to what comes before that symbol.
One candidate for the separator is "!/". With this separator, a URL that refers to a file main.js inside a package looks like this:
http://example.com/app.pack!/impl/main.jsThe following two tags use package URLs. The first starts a web application, the second displays a logo. In both cases, the files are inside the package.
<script src="app.pack!/impl/main.js"> <img src="app.pack!/img/logo.jpg">The nice thing about packaging is that old servers and browsers can use it, too:
- Old browsers send separate requests for each file in a package.
- New browsers download the package once and afterwards extract files as necessary.
- Old servers store both separate files and the package and serve either one, depending on what browsers request.
- New servers could create the package on demand.
Intriguingly, packages could become a cross-browser format for archiving web pages (including images, CSS, etc.).
How to best set up modules to be loaded from a package is still being worked out. At the very least you can plug into the module loader API and set it up manually.
In its inline version, the <module> tag nicely cleans up the problematic <script> tag:
- Strict mode is on by default, avoiding the visual clutter of 'use strict';
- The code runs asynchronously.
- The code runs in its own scope, there is no danger of polluting the global scope.
Sources of this post
- “Modules: Status Update”, slides by David Herman.
- “Modules vs Scripts”, an email by David Herman.
- “Packages”, a Gist by Yehuda Katz.