这个笔记记录 Node.js 引入模块的两种方案及其语法。
CommonJS 是 Node.js 默认使用的模块导出和引入方式,使用 module.exports
和 require
关键词。注意 exports
只是 module.exports
的替身,真正被导出的是 module.exports
对象。
const module = { exports: {} };
const exports = module.exports;
return module.exports;
早在 2017 年,Node.js 8.9.0 就开始探索性地通过 --experimental-modules
支持 ESM,但当时 .js
后缀还无法使用 export
和 import
,需要把后缀改为 .mjs
。2019 年 Node.js 提出了一套新的 ESM 方案,这时 .js
终于支持 export
、 import
,新方案还在 package.json
增加了 "type":"module"
配置项。新方案从 Node.js 12 开始支持,但依然需要 --experimental-modules
开启。
2019 年 11 月 Node.js 13.2.0 发布,执行 ESM 终于不再需要 --experimental-modules
,但会在 console 打印 ExperimentalWarning: The ESM module loader is experimental.
警告信息。
2020 年 4 月发布的 Node.js 14 删除了这条警告信息,但官方表示 ESM 依然处于 experimental 阶段。直到 2020 年 11 月发布的 Node.js 15.3.0 才终于把对 ESM 的支持标记为 stable 状态。
Node.js 的 LTS 版本号都是偶数,官方也建议大多数开发者使用 LTS 版本,大多数云函数服务内置的 Node.js runtime 也是 12.x、14.x、16.x 这些偶数版本,所以我们可以认为从 Node.js 16 开始,我们才终于可以放心地使用 ESM。
You also need to make sure you're on the latest minor version of Node.js. At minimum Node.js 16. 出处
下面是一个 ES module,这个例子展示了 export 的多种方法:
const a = 1;
export default a; //default export
export const b = 2; //name export 一个变量
const c = 3;
const d = 4;
export { c as _c, d }; //name export 多个变量,注意 as 关键词
export * as utilities from "./utilities"; //re-export
以下是对这个 ES module 的多种 import 方法:
import module from "./module.mjs"; //这样写会拿到 default export
console.log(module); //1
import * as module from "./module.mjs"; //这样写会拿到 export 的所有值
console.log(module.default); //1
console.log(module.a); //undefined
console.log(module.b); //2
console.log(module.c); //undefined
console.log(module._c); //3
import { b, _c } from "./module.mjs"; //这样写会拿到部分值
console.log(b, _c); //2, 3
import { b as _b, _c as __c } from "./module.mjs"; //改名
console.log(_b, __c); //2, 3
Node.js 默认使用 CommonJS,要开启 ESM,主要有两种方法:
.mjs
,只有 .mjs
文件会变成 ESM,其他都是 CommonJS;"type":"module"
,除了 .cjs
其他都是 ESM;sindresorhus 用的是第二种方案,但他不会兼容 CommonJS,所以不会有 .cjs
。
ES modules、CommonJS 的部分区别:
__dirname
和 __filename
,可以用 import.meta.url
;require.resolve
,可以用 import.meta.resolve
;有时候不需要 import 什么东西,只是需要执行这个 module,这时候可以使用 empty import。
Empty import: only loads the module, doesn’t import anything. The first such import in a program executes the body of the module. 出处
这些 module 也不会 export 什么东西,只是可能会给全局变量增加一些属性。
Some modules do not export any variables and only have side-effects, such as mutating the global window (global variables) or prototypes (e.g. polyfills). To execute the body of these modules, they can be imported without specifying any variable names. It will be executed only once, because modules in JavaScript are singletons. 出处
在 React 应用中我们经常 empty import 一个 css 文件,再比如使用 prism
:
import "prismjs";
import "prismjs/components/prism-bash.min";
import "prismjs/components/prism-javascript.min";
import "prismjs/components/prism-json.min";
import "prismjs/components/prism-jsx.min";
相关笔记: