Node.js 模块系统:CJS 与 ESM 详解

Node.js 模块系统:CJS 与 ESM 详解
文章目录前言一、CommonJS (CJS)二、ESMES Module三、跨模块互相导入重要坑点四、有关路径的要求总结前言Node.js 两套模块系统CommonJS(CJS)、ESM由package.json和文件后缀共同决定。JavaScript 最初没有模块系统Node.js 诞生后自行实现 CommonJSrequire/module.exports用于服务端并非语言官方标准。随后ES6 推出官方标准 ESMimport/export支持浏览器与 Node支持静态分析但 Node 为兼容海量旧生态没有直接替换 CommonJS。于是 Node 两套模块共存依靠package.json type和文件后缀.mjs/.cjs区分由此产生各类导入语法差异。一、CommonJS (CJS)要求如下package.json: “type”: “commonjs” 默认值不写type也是CJS文件后缀.js - CJS; .cjs - 强制 CJS无视package.json语法require() / module.exports不能用import/export导出helpers.js// 方式1整体导出对象最常用module.exports{func1,func2};// 方式2逐个挂载exports.func1func1;exports.func2func2;导入main.js// ✅ 本地文件必须 ./ 开头后缀 .js 可以省略建议写上const{func1,func2}require(./helpers.js);// ❌ 错误不带 ./node会去node_modules查找npm包const{func1}require(helpers);require(./helpers)省略后缀也可以Node 自动补.js/.json。只能用.js后缀不能命名为.mjs。运行命令node main.js二、ESMES Modulepackage.jsontype:module文件后缀.mjs→ 强制 ESM完全忽略 package.json 的 type 配置语法import / export原生不能直接用require()重点.mjs永远 ESM.cjs永远 CJS普通.js看package.json的type字段。导出helpers.js:// 方式1声明时直接导出exportasyncfunctionloadPackage(){}// 方式2末尾集中导出export{loadPackage};// 默认导出exportdefault{loadPackage};导入main.mjs:// ✅本地文件必须 ./ 开头**后缀 .js 不能省略ESM不会自动补后缀**import{loadPackage}from./helpers.js;// 如果导入的是CJS模块helpers.js是module.exports也可以default导入importhelpersfrom./helpers.js;const{loadPackage}helpers;// ❌错误1无 ./当作npm包import{loadPackage}fromhelpers;// ❌错误2省略后缀ESM直接报模块找不到import{loadPackage}from./helpers;当文件是.mjs就算 package.json 是commonjs依然执行 ESM 语法。运行node generateTestcase.mjs三、跨模块互相导入重要坑点ESM (.mjs) 导入 CJS (.js):允许CJS 的module.exports对象被 ESM 识别支持解构导入。import{loadPackage}from./helpers.js;// 结构导入// 或者使用CJS的原生require导入import{createRequire}frommodule;constrequirecreateRequire(import.meta.url);// Enable require in ESMconstpkgrequire(pkgPath);CJS 导入 ESMCJS 的require()不能直接 require ESM 文件会报错只能用动态await import()。// CJS里面加载ESM模块只能动态importconstesmModuleawaitimport(./some-esm.js);四、有关路径的要求ESM 动态导入await import其路径必须是file://开头的 URL 字符串指向本地磁盘文件。不接受windows系统的反斜杠。// ✅ 本地磁盘文件必须转成file:// URLconsturlpathToFileURL(absDiskPath).href;constmodawaitimport(url);constabsFsPathC:\\Users\\LIly\\file.js;// path.resolve得到带反斜杠 const url pathToFileURL(absFsPath).href; // ✅API内部自动处理反斜杠 → file:///C:/... const mod await import(url);awaitimport(./dir/test.js);// ✅ 只能正斜杠 await import(./dir\\test.js);// ❌ 反斜杠不行同静态import// Windows反斜杠转换functionnormalizeSlash(p){if(isWindows()){returnp.replace(/\\/g,/);}returnp;}ESM 静态导入 import xxx from “xxx”静态导入不接受 file:// URL也不接受操作系统磁盘绝对路径C:\xxx /home/xxx。不接受Windows的反斜杠。静态导入只有两类合法输入裸模块名npm 包、node 内置模块lodash、fs/promisesimportxfromlodash相对说明符./xxx.js、…/xxx.jsimportxfrom./dir\\test.js;// ❌错误\是字符串转义符号路径直接错乱importxfrom./dir/test.js;// ✅只能正斜杠 /require的动态导入操作系统原生磁盘路径完全接纳 Windows 反斜杠\唯一坑给 require 的相对本地文件路径必须带上./或者../否则会被识别成npm包。letpath./helper.js;constmrequire(path);// ✅完全合法if(flag){require(./other.js)}总结Node.js 中 CJS 与 ESM 双模块系统共存核心区分逻辑是package.json的type字段 文件后缀.mjs/.cjsCJSrequire/module.exportstype: commonjs或默认require对本地文件路径带./后缀.js可省略。ESMimport/exporttype: module或.mjs后缀强制import必须写./且后缀不可省略。跨模块导入ESM 导入 CJS 一般没问题支持解构导入反过来 CJS 用require加载 ESM 会直接报错必须用动态await import()。日常开发先确认项目根目录package.json的type再决定用哪个文件后缀和导入语法可以避免绝大多数模块解析错误。导入方式类型执行时机静态 / 动态路径完整要求Windows 反斜杠支持相对路径要求CommonJSrequire()运行时函数调用执行到该行才加载✅动态导入1. npm 包直接写包名2. 本地文件操作系统原生磁盘路径不需要 file:// 协议✅兼容\与/本地相对文件必须带.//../不带则识别为 npm 包后缀可省略ESM 静态导入import xxx from xxxJS 语法解析阶段代码运行前✅静态导入1. npm 包直接写包名2. 本地文件仅允许.//../相对说明符禁止 file://、禁止磁盘绝对路径只能字符串字面量不能变量❌禁止\只能正斜杠/必须.//../前缀必须写完整.js后缀不可省略ESM 动态导入await import(xxx)Promise 函数调用执行到该行才加载✅动态导入1. npm 包直接写包名2. 本地磁盘文件必须传入file://URL不能直接传操作系统磁盘路径支持变量传参❌不要手动处理\原始磁盘路径交给pathToFileURL()自动转 URL字面量写./xxx.js规则同静态导入变量加载本地文件必须转为file://

最新新闻

日新闻

周新闻

月新闻