Module loading implementation
- 1. Browser loading
-
- traditional method
- load rule
- Differences between ES6 modules and CommonJS modules
-
- 1. CommonJS modules output a copy of a value, while ES6 modules output a reference to a value.
- 2. CommonJS modules are loaded at runtime, and ES6 modules are output interfaces at compile time.
- 3. The require() of the CommonJS module loads the module synchronously, and the import command of the ES6 module loads asynchronously, and there is an independent parsing phase of module dependencies.
- 3. Node.js module loading method
1. Browser loading
traditional method
In HTML web pages, browsers <script>
load JavaScript scripts through tags.
<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
// module code
</script>
<!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>
By default, the browser loads JavaScript scripts synchronously, that is, the rendering engine <script>
will stop when it encounters a tag, wait until the script is executed, and then continue to render down. If it is an external script, the script download time must also be added. If the script is large in size, it will take a long time to download and execute, thus causing the browser to be blocked, and the user will feel that the browser is "stuck" without any response. The user experience is very poor.
So browsers allow scripts to be loaded asynchronously, here are two syntaxes for asynchronous loading.
<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>
defer
or async
attribute, the script will load asynchronously. When the rendering engine encounters this line of command, it will start to download the external script, but it will not wait for it to download and execute, but directly execute the following command.
defer
async
The difference with:
-
defer
It will not be executed until the entire page is rendered normally in memory (the DOM structure is completely generated, and other scripts are executed); -
async
Once the download is complete, the rendering engine will interrupt the rendering, and continue rendering after executing this script.
Summarize: defer
It is "execute after rendering", async
and "execute after downloading". In addition, if there are multiple defer
scripts, they will be loaded in the order in which they appear on the page, and multiple async
scripts cannot guarantee the loading order.
load rule
The browser loads ES6
the module, which also uses <script>
tags, but adds type="module"
attributes.
<script type="module" src="./foo.js"></script>
ES6 modules also allow embedding in web pages, and the syntax and behavior are exactly the same as loading external scripts.
<script type="module">
import utils from "./utils.js";
// other code
</script>
Precautions:
- Code is run at module scope, not at global scope. Top-level variables inside the module, not visible outside.
- Module scripts automatically adopt strict mode, whether declared or not
use strict
. - Among the modules, you can use
import
commands to load other modules (.js
the suffix cannot be omitted, you need to provide absolute or relative URLs), and you can also useexport
commands to output external interfaces. - Within a module, the top-level
this
keyword returnsundefined
, not points towindow
. In other words, it is meaningless to use the this keyword at the top level of the module. - If the same module is loaded multiple times, it will only be executed once.
Differences between ES6 modules and CommonJS modules
1. CommonJS modules output a copy of a value, while ES6 modules output a reference to a value.
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
get counter() {
return counter
},
incCounter: incCounter,
};
2. CommonJS modules are loaded at runtime, and ES6 modules are output interfaces at compile time.
3. The require() of the CommonJS module loads the module synchronously, and the import command of the ES6 module loads asynchronously, and there is an independent parsing phase of module dependencies.
3. Node.js module loading method
JavaScript now has two kinds of modules. One is ES6
module, abbreviated ESM
; the other is CommonJS
module, abbreviated CJS
.
CommonJS
The module is Node.js
proprietary and ES6
incompatible with the module. Above the grammar, the most obvious difference between the two is that CommonJS
the module uses require()
and module.exports
, and ES6
the module uses import
and export
.
Node.js
Requires ES6
modules to take .mjs
the suffix filename. import
In other words, as long as the or command is used in the script file export
, the suffix name must be used .mjs
. When Node.js encounters .mjs
a file, it considers it to be an ES6 module. Strict mode is enabled by default and does not have to be specified at the top of each module file "use strict"
.
If you don't want to change the suffix name to , you can specify the field as in the .mjs
project file .package.json
type
module
{
"type": "module"
}
Once set, the project's JS scripts are interpreted as ES6 modules.
# 解释成 ES6 模块
$ node my-app.js
If you want to use the CommonJS module at this time, you need to change the suffix name of the CommonJS script to .cjs
. If there is no type
field, or type
if the field is commonjs
, the .js script will be interpreted as CommonJS
a module.
Summarize: .mjs
The file is always loaded as an ES6 module, and the file is always loaded .cjs
as a module, and the loading of the file depends on the settings of the fields inside .CommonJS
.js
package.json
type
CommonJS modules load ES6 modules
CommonJS require()
commands cannot load ES6 modules, and an error will be reported, so import()
this method can only be used to load them.
// 在 CommonJS 模块中运行
(async () => {
await import('./my-app.mjs');
})();
require()
One reason why ES6 modules are not supported is that they are loaded synchronously, and top-level commands can be used inside ES6 modules await
, so they cannot be loaded synchronously.
ES6 modules load CommonJS modules
ES6 module import
commands can load CommonJS
modules, but only as a whole, not just a single output item.
// 正确
import packageMain from 'commonjs-package';
// 报错
import {
method } from 'commonjs-package';
This is because ES6 modules need to support static code analysis, while the output interface of CommonJS modules is module.exports
an object that cannot be statically analyzed, so it can only be loaded as a whole.
Loading a single output item can be written as follows.
import packageMain from 'commonjs-package';
const {
method } = packageMain;
There is also an alternative loading method, which is to use the built-in module.createRequire()
method of Node.js.
// cjs.cjs
module.exports = 'cjs';
// esm.mjs
import {
createRequire } from 'module';
const require = createRequire(import.meta.url);
const cjs = require('./cjs.cjs');
cjs === 'cjs'; // true
In the above code, the ES6 module module.createRequire()
can load CommonJS
the module through the method. However, this way of writing is equivalent to mixing ES6
and , so it is not recommended to use.CommonJS