Appearance
Node.js中模块化方案
CommonJS 和 ESM 是 JavaScript 中两种不同的模块系统
1. CommonJS
require() 导入、module.exports 或 exports 导出
项目默认是 CommonJS 规范,指定文件后缀为 .cjs后,此文件会遵守CommonJS 规范。
2. ES Module
(也被称为ES6 Module、ECMA Modules、ESM)
import 导入、export ( export default ) 导出
在Node.js中使用ESM,需要在package.json中设置"type": "module",或者文件名后缀为.mjs。
区别
ES module 是值的引用,CommonJS 是值的拷贝。
- ES module:导出的是值的引用。如果模块中修改了导出变量的值,那么所有导入这个变量的模块都会看到变化。这是因为 ES module(静态导入) 在编译时就确定了模块的依赖关系,并在运行时通过引用直接访问导出的值。
export let a = 10
setTimeout(() => {
a = 11
}, 1000)
// 引入模块
import { a } from './1.js'
console.log(a) // 10
setTimeout(() => {
console.log(a) // 11
}, 1500)
- CommonJS:导出的是值的拷贝(浅拷贝)。如果在CommonJS模块中修改了导出变量的值,这个变化不会反映到已经require了这个模块的其他模块中。这是因为 CommonJS(动态导入) 是在运行时加载的,每个require调用都会得到一个值的拷贝,修改这个拷贝不会影响原始值。
exports.a = 10;
setTimeout(() => {
a = 11
},1000)
// 引入模块
let { a } = require('./1.js')
console.log(a) // 10
setTimeout(() => {
console.log(a) // 10
}, 1500)
执行时机
ES module
模块加载和执行是异步的;
模块的执行顺序取决于模块之间的依赖关系和浏览器的加载策略;
import 不会阻塞代码执行,浏览器会在后台加载模块;
CommonJS
模块加载和执行是同步的;
模块的执行顺序与它们在代码中出现的顺序是一致的;
require() 会阻塞代码执行,直到模块加载完成;
兼容性
ES module
是 JavaScript 的标准模块系统;
现代浏览器都原生支持 ES 模块;Node.js 也支持
CommonJS
主要用于服务器端,Node.js正是它的代表;
两种模块方案互相加载
- CommonJS 加载 ESM
const tm = await import('./index.mjs')
- ESM 加载 CommonJS
import tm from'./index.cjs'
或
import('./index.cjs').then((myModule)=>{ // 动态导入,返回一个 Promise
console.log(myModule);
})
同时支持两种模块方式
咱们开发npm包时,可能需要同时适配 CommonJS 和 ES Module:
- 在package.json中使用 "main" 和 "module" 字段分别指定 CommonJS 和 ES Module的入口文件
{
"main": "lib/index.js",
"module": "src/index.mjs"
}
- package.json中设置 exports 字段
"exports":{
"require": "./index.js",
"import": "./esm/index.js"
}
全局变量获取
ESM环境中,传统的 CommonJS 全局变量 __dirname 和 __filename 不再直接可用。 这是因为 ES Module 采用不同的模块解析策略,更加符合 ECMAScript 标准。
ESM 中获取__dirname、__filename:
import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(fileURLToPath(import.meta.url))
- import.meta: 当前模块信息的对象
- fileURLToPath():将 URL 字符串转换为对应的本地文件系统路径
- dirname():接受一个文件路径作为参数,返回该路径的目录部分
规范文档:
https://nodejs.cn/api/modules.html#modules_modules_commonjs_modules