概念
Node.js 里可分为 CommonJS 模块和 ECMAScript 模块(ESM)两种不同的模块系统。
CommonJS 模块是 Node.js 最初支持的模块系统,它使用 require() 函数来导入模块,使用 module.exports 或 exports 对象来导出模块。这种模块系统通常只能在 Node.js 环境下使用,并且不允许在浏览器环境中使用。
ECMAScript 模块是 JavaScript 的标准模块系统,它使用 import 和 export 关键字来导入和导出模块。它可以在 Node.js 环境下和现代浏览器环境中使用,具有更好的跨平台兼容性和可移植性。Node.js 从版本12开始支持 ECMAScript 模块作为实验性功能,并在版本14中正式支持。
如何将 js 文件变成 mjs 模块
- 将
package.json的type属性的值改成module - 执行命令
node xxx.js --input-type=module
cjs 引入 mjs 并使用
- 安装第三方插件
npm install esm
// test.mjs
export const name = "berserker";
export default "fate";
// index.js
require = require("esm")(module);
const Test = require("./test.mjs");
console.log(Test); // { name: 'berserker', default: 'fate' }
首先安装 esm 模块依赖包, 传入 module 对象 创建一个新的 require,接着就可以使用 esm 模块文件了
- 采用动态加载
import('test.mjs').then(res => {
console.log(res) // { default: 'fate', name: 'berserker' }
})
mjs 引入 cjs 并使用
在 Node.js 中,使用 ECMAScript 模块(ESM)的文件通常使用 .mjs 扩展名。要在 .mjs 文件中引入 CommonJS 模块(CJS),可以使用 Node.js 提供的 createRequire() 函数。
例如,在 .mjs 文件中可以这样引入一个 CJS 模块:
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const cjsModule = require('./cjs-module');
- 示例
// test.js
const saber = "saber";
const berserker = "berserker";
exports.saber = saber;
exports.berserker = berserker;
module.exports = "fate"; // 猜猜 去掉这行 和 不去掉这行 分别打印什么
// index.mjs
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const Test = require("./test.js");
console.log(Test);
额外知识
esm 模块 里没有 __dirname 和 __filename,也不能导入 json 文件,可以改写一下:
import url from "node:url";
import path from "node:path";
import { createRequire } from "node:module";
Object.defineProperty(global, "loadJSON", {
get() {
return (filepath, importMetaUrl = import.meta.url) => {
const reg = /\S+.json$/g;
if (reg.test(filepath)) {
const require = createRequire(importMetaUrl);
return require(filepath);
} else {
throw new Error("loadJSON 的参数必须是一个json文件");
}
};
},
enumerable: true,
configurable: false,
// writable: false,
});
Object.defineProperty(global, "getFileName", {
get() {
return (importMetaUrl = import.meta.url) => {
return url.fileURLToPath(importMetaUrl);
};
},
enumerable: true,
configurable: false,
// writable: false,
});
Object.defineProperty(global, "getDirName", {
get() {
return (importMetaUrl = import.meta.url) => {
return path.dirname(url.fileURLToPath(importMetaUrl));
};
},
enumerable: true,
configurable: false,
// writable: false,
});
总结
现在已经有很多 npm 包已逐渐采用 ESM 规范开发,日后 浏览器 和 Node 肯定会保持相同的规范达成天下大同,学习 mjs 和 cjs 的相互使用,也是有必要的一个过程。文中都是笔者的实践经验总结,有不足的地方欢迎指正和补充。