ECMAScript Modules in Browsers

By Jake Archibald

Translator: Zhang Yi

Proofreading: Bolun Yu

Please indicate the source of reprint.

ES Modules is coming to all browsers! The following browser versions are supported:

  • Safari 10.1.
  • Chrome Canary 60 — Set the Experimental Web Platform flag parameters on the Chrome: Flags Settings page.
  • Firefox 54 – in about: config Settings page in the dom. ModuleScripts. Relevant parameter Settings enabled.
  • Edge 15 — Set parameters for the Experimental JavaScript Features on the About: Flags page.
<script type="module">
  import {addTextToBody} from './utils.js';

  addTextToBody('Modules are pretty cool.');
</script>
Copy the code
// utils.js
export function addTextToBody(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}
Copy the code

The demo page

All you need to do is add the type=module attribute to the script tag so that the browser treats the script you import or write as an ECMAScript module.

There are some great modules articles on the web, but I’d like to share some browser-specific modules features THAT I’ve read and tried:

Import import path support is incomplete

/ / support path format as follows: the import {foo} from 'https://jakearchibald.com/utils/bar.js'. import {foo} from '/utils/bar.js'; import {foo} from './bar.js'; import {foo} from '.. /bar.js'; // The following formats are not supported: import {foo} from 'bar.js'; import {foo} from 'utils/bar.js';Copy the code

The path to import modules must meet one of the following conditions:

  • The new URL(moduleSpecifier) can be used to eliminate errors.
  • Starts with /.
  • Starts with a./.
  • In order to.. / at the beginning.

Other path formats may be supported in the future, such as importing built-in modules.

Backward compatibility is introduced with the nomodule attribute

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>
Copy the code

The demo page

Browsers that support the type=module attribute can ignore script tags with the nomodule attribute. In the example above, the script for the former TAB will be loaded if the browser supports module loading, while other browsers that do not support module loading will load the latter script named fallback.js.

Browser Legacy issues

  • Firefox does not support nomodule (issue). This has been fixed in this nightly edition!
  • Edge does not support nomodule (issue).
  • Nomodule is not supported in Safari 10.1, but it was fixed in the latest technical preview. There is a great support scheme in version 10.1.

The default delay

<! -- This script will execute after... --> <script type="module" src="1.js"></script> <! -... This script... --> <script src="2.js"></script> <! -... but before this script. --> <script defer src="3.js"></script>Copy the code

The demo page order should be 2.js, 1.js, 3.js.

Script blocking HTML loading is a very bad situation. You can add a defer attribute to the script tag to prevent page blocking and also make the script run only after the document has been parsed. By default, module type scripts load similarly to the scripts that added the defer attribute — there’s no reason they should block the page loading, after all.

The Module script uses the same execution queue as the script that added the defer attribute.

Inline scripts also load lazily

<! -- This script will execute after... --> <script type="module"> addTextToBody("Inline module executed"); </script> <! -... This script... --> <script src="1.js"></script> <! -... And this script... --> <script defer> addTextToBody("Inline script executed"); </script> <! -... but before this script. --> <script defer src="2.js"></script>Copy the code

The demo page loading sequence is 1.js, inline script, inline module, 2.js.

Typically, inline scripts ignore the defer attribute, and in the meantime, the inline Module script is lazily loaded regardless of whether or not it introduces anything.

How does async for external and inline Modules work

<! -- This executes as soon as its imports have fetched --> <script async type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Inline module executed.'); </script> <! -- This executes as soon as it & its imports have fetched --> <script async type="module" src="1.js"></script>Copy the code

The faster scripts downloaded from the demo page run first.

The async property enables scripts to run immediately after downloading without blocking HTML loading. Unlike regular scripts, Async also works with inline Modules.

When we use async, scripts may not execute in the order in which they are written in the DOM.

Browser Legacy issues

  • Firefox does not support Async (Issue) on inline module scripts.

Module is executed only once

<! -- 1.js only executes once --> <script type="module" src="1.js"></script> <script type="module" src="1.js"></script> <script type="module"> import "./1.js"; </script> <! -- Whereas normal scripts execute multiple times --> <script src="2.js"></script> <script src="2.js"></script>Copy the code

The demo page

If you’re familiar with ES Modules, you should know that we can use import multiple times, but module will only be executed once. This feature also applies to module scripts in HTML, which are imported from a URL and executed only once on a page.

Browser Legacy issues

*Edge can execute modules multiple times.

Modules are loaded as cross-domain resource sharing

<! -- This will not execute, as it fails a CORS check --> <script type="module" SRC ="https://... .now.sh/no-cors"></script> <! -- This will not execute, as one of its imports fails a CORS check --> <script type="module"> import 'https://... .now.sh/no-cors'; addTextToBody("This will not execute."); </script> <! <script type="module" SRC ="https://... .now.sh/cors"></script>Copy the code

The demo page

Unlike regular scripts, Module scripts are loaded through cross-domain resource sharing. This means that loaded Module scripts must return the correct CORS header, such as access-Control-Allow-Origin: *.

Browser Legacy issues

  • Firefox cannot load the demo page.
  • Edge load demo page without CORS head ([issue [(developer.microsoft.com/en-us/micro…

Don’t take the credentials

<! -- Fetched with credentials (cookies etc) --> <script src="1.js"></script> <! -- Fetched without credentials --> <script type="module" src="1.js"></script> <! -- Fetched with credentials --> <script type="module" crossorigin src="1.js?" ></script> <! -- Fetched without credentials --> <script type="module" crossorigin src="https://other-origin/1.js"></script> <! -- Fetched with credentials--> <script type="module" crossorigin="use-credentials" src="https://other-origin/1.js?" ></script>Copy the code

The demo page

Most CORS based apis send credentials (cookies, etc.) if the request is from the same source, but fetch () and module scripts are exceptions — they don’t carry credentials by default.

You can make the same origin Module carry credentials by adding the Crossorigin attribute (which I found a bit strange and have questioned in spec discussions). In the non-same-origin case, you can set crossorigin=”use-credentials”. Note that the source of the request must have a response containing the access-Control-allow-credentials: true header.

There is also a complication related to the “Module only executes once” rule. Each module is tagged according to the URL from which it was introduced, so if you first request a module without credentials, then you still get the previous module without credentials, which is why I appended “? “to the URL in the example above. To ensure that the module is loaded uniquely.

Browser Legacy issues

  • Chrome requires modules (issues) with credentials from the same source.
  • Safari requires modules with no credentials and the same source, even if you use the Crossorigin attribute (issue).
  • Edge will send credentials to the same source by default, but will not issue if the Crossorigin attribute is added.

Firefox is the only one that does it well.

Mime-types

Unlike regular scripts, Module scripts must use one of the valid JavaScript MIME types or they will not execute.

The demo page

Browser Legacy issues

  • Edge executes scripts (issues) using invalid MIME types.

That’s all I’ve learned so far about browser support for ES Modules, not to mention how excited I am about it.