Javascript Modules in the Browser – script type=module
In this note we will talk about using ES6 modules in the browser. These are standard ES6 modules. Only they are used in code intended for browsers.
To tell the browser that the loaded script is a module, you need to add the attribute type="module":
<script type="module" src="https://dom/script.js"></script>
A module can also be inline script:
<script type="module">
export const doSomething = message => {
console.log('No.');
}
</script>
Browser support
The main question - how modules are currently supported in the browser. As of today support is already excellent - all major browsers support modules. Therefore you can already use:
How to connect a WordPress script as a module
Since WP 6.5 you can use special functions:
- wp_register_script_module() — Registers the script module if no script module with that script module identifier has already been registered.
- wp_enqueue_script_module() — Marks the script module to be enqueued in the page.
- wp_dequeue_script_module() — Unmarks the script module so it is no longer enqueued in the page.
- wp_deregister_script_module() — Deregisters the script module.
Example:
add_action( 'wp_enqueue_scripts', 'my_enqueue_modules' );
function my_enqueue_modules() {
$src = plugins_url( 'assets/js/app.js', __FILE__ );
wp_enqueue_script_module( 'my_module', $src, [], '1.0.0', [ 'in_footer' => true ] );
}
Before WP 6.5
You can use the [script_loader_tag] hook:
add_filter( 'script_loader_tag', 'scripts_as_es6_modules', 10, 3 );
function scripts_as_es6_modules( $tag, $handle, $src ) {
if ( 'my-script' === $handle ) {
return str_replace( '<script ', '<script type="module"', $tag );
}
return $tag;
}
Features and capabilities of modules
Always uses “use strict”
For example, assigning to an undeclared variable will throw an error.
<script type="module"> a = 5; // error </script>
Separate variable scope
Each module has its own scope. In other words, variables and functions declared in the module are not visible in other scripts:
<!doctype html> <script type="module"> // Variable is available only in this module let user = "John"; </script> <script type="module"> alert( user ); // Error: user is not defined </script>
However, the module itself sees variables from the parent scope:
<!doctype html> <script> let user = "John"; </script> <script type="module"> alert( user ); //> John </script>
If a global page-wide variable is needed, it is better to explicitly assign it to the window object. But such an approach should be an exception requiring a strong reason.
<!doctype html> <script> window.user = "John"; </script> <script type="module"> alert( user ); //> John </script>
Modules should export functionality intended for use by others. And other modules can import it.
<!doctype html> <script type="module" src="user.js"></script> <script type="module" src="hello.js"></script>
// user.js export let user = "John";
// hello.js
import {user} from './user.js';
Module code executes once (on import)
If the same module is used in several places, its code will execute only once, after which the exported functionality is passed to all importers.
In practice, the module's task is usually initialization – creating internal data structures. And if we want something that can be used many times, we export it.
More here: https://learn.javascript.ru/modules-intro#kod-v-module-vypolnyaetsya-tolko-odin-raz-pri-importe
import.meta
The object import.meta contains information about the current module.
Content depends on the environment. In the browser it contains a link to the script or a link to the current web page if the module is embedded in HTML:
<script type="module"> alert(import.meta.url); // link to html page for embedded script </script>
this in a module is not defined
In a module, at the top level, this is not defined (undefined):
<script> alert(this); // window </script> <script type="module"> alert(this); // undefined </script>
Browser-specific features
Deferred module loading
Modules always execute in a deferred manner, just like scripts with the defer attribute. This works for external and inline scripts.
In other words:
-
Loading external modules, such as
<script type="module" src="...">>, does not block HTML processing. -
A module, even if loaded quickly, waits for the full loading of the HTML document, and only then executes.
- The relative order of scripts is preserved: scripts earlier in the document execute earlier.
async allows not delaying module execution and works for inline scripts
For ordinary scripts the async attribute will work only if a file is loaded - not an inline script.
For modules, the async attribute works for both files and inline scripts. And also, it makes the module's code execute immediately after loading, and not in order.
For example, the script below will perform the import (load ./analytics.js) and will start immediately when ready, even if the HTML document is not yet loaded, or if other scripts are still loading.
<script async type="module">
import {counter} from './analytics.js';
counter.count();
</script>
This is very useful when a module is not connected to anything else, for example for counters, event handlers, ads.
Identical external scripts run once
External scripts with the same src attribute run only once:
<!-- script my.js will load and be executed only once --> <script type="module" src="my.js"></script> <script type="module" src="my.js"></script>
External script from another domain requires CORS
If a module script is loaded from another domain, the remote server must set the header Access-Control-Allow-Origin indicating that loading the script is allowed.
<!-- another-site.com should specify the Access-Control-Allow-Origin header --> <!-- otherwise, the script will not be executed --> <script type="module" src="http://another-site.com/their.js"></script>
This provides better security by default.
Bare modules are not allowed
In the browser, import must contain a relative or absolute path to the module. Bare modules are not allowed in import.
For example, this import is incorrect:
import {sayHi} from 'sayHi'; // Error, "bare" module
// a path should be, for example './sayHi.js' or absolute
Other environments, such as Node.js, allow the use of bare modules without paths, since they have their own rules for how to work with such modules and where to find them. But browsers do not yet support bare modules.
--