尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Node.js 导入导出实战:CommonJS 与 ES6 模块配置避坑指南

发布时间:2026/9/27 22:43:50

资讯中心
01
ARTICLE

Node.js 导入导出实战:CommonJS 与 ES6 模块配置避坑指南

Node.js 导入导出实战:CommonJS 与 ES6 模块配置避坑指南
1. 为什么你的 Node.js 项目总在 require 和 import 之间报错刚接触 Node.js 模块化开发时最容易卡住的地方不是写业务逻辑而是文件之间的导入导出。你写了一个工具函数想在其他文件里用结果终端甩出一句Cannot use import statement outside a module或者require is not defined in ES module scope然后你就开始怀疑人生。这个问题的根源在于 Node.js 同时存在两套模块系统CommonJS 和 ES6ECMAScript Modules简称 ESM。CommonJS 是 Node.js 早期的默认方案用require()导入、module.exports导出ES6 模块是 ECMAScript 标准方案用import导入、export导出。两套系统语法不同、加载机制不同甚至同一个.js文件在不同配置下会被当成不同的模块类型来解析。适合阅读这篇文章的人刚学 Node.js 后端开发、对模块化概念还比较模糊、遇到ERR_REQUIRE_ESM或Cannot use import statement outside a module不知道怎么排查的新手。我会用可复制的package.json配置和.mjs/.cjs文件骨架把两种模块规范的导入导出方式讲清楚再给出混用报错的逐步验证动作让你一次跑通两种写法。在开始之前如果你需要调用大模型 API 来做一些模块化的 AI 功能测试可以先用 TaoToken 的模型对话 快速验证接口返回格式确认请求参数和响应结构没问题之后再把它封装成 Node.js 模块集成到项目里。这样调试成本会低很多。2. 先把环境配好TaoToken 前置准备在写模块化代码之前如果你打算在项目里接入大模型能力比如做一个翻译模块、摘要模块需要先拿到 API Key。这一步很快但后面所有请求都依赖它。打开 TaoToken 控制台注册或登录后进入 API Keys 管理页创建一个新的 Key。建议按项目命名比如node-module-demo方便后续区分。拿到 Key 之后你的 Node.js 项目里可以通过环境变量读取不要硬编码在源码里。下面是一个.env文件的示例TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在代码里用process.env.TAOTOKEN_API_KEY读取。这样无论你用 CommonJS 还是 ESM都能统一管理配置。如果你后续要做长期编码或者 Agent 类项目可以考虑 Coding Plan它更适合需要持续调用模型的场景。接入细节可以参考 接入文档。3. CommonJS 与 ES6 模块的可复制配置骨架3.1 package.json 里的 type 字段决定一切Node.js 判断一个.js文件用哪种模块系统核心看package.json里的type字段。默认不写type或者写type: commonjs.js文件就按 CommonJS 解析写type: module.js文件就按 ES6 模块解析。下面是一个完整的package.json骨架你可以直接复制{ name: node-module-demo, version: 1.0.0, type: commonjs, scripts: { start:cjs: node src/cjs/app.js, start:mjs: node src/mjs/app.mjs }, dependencies: { dotenv: ^16.4.5 } }这里type: commonjs表示默认走 CommonJS。如果你想整个项目走 ES6 模块把值改成module即可。但更推荐的做法是用文件扩展名来区分.cjs强制 CommonJS.mjs强制 ES6 模块.js则跟随package.json的type字段。这样同一个项目里两种模块可以共存不会互相干扰。3.2 CommonJS 导出与导入写法CommonJS 的导出核心是module.exports。它可以导出字符串、对象、函数也可以挂载多个属性。先看一个导出对象的例子// src/cjs/message.cjs module.exports Hello World!;导入时用require()// src/cjs/app.cjs const msg require(./message.cjs); console.log(msg); // 输出: Hello World!如果导出多个属性可以这样写// src/cjs/person.cjs module.exports.name Sachin; module.exports.age 20; module.exports.greet function (name) { console.log(Welcome, name); };导入后直接访问属性或调用方法// src/cjs/use-person.cjs const person require(./person.cjs); console.log(person.name, ,, person.age); // Sachin , 20 person.greet(Sachin); // Welcome Sachin注意一个容易踩的坑exports和module.exports初始指向同一个对象但如果你直接给exports赋值一个新对象它就不再和module.exports关联了。所以导出单个函数或对象时统一用module.exports ...最稳妥。// src/cjs/area.cjs function area(x) { return x * x; } module.exports { area };// src/cjs/use-area.cjs const square require(./area.cjs); console.log(square.area(5)); // 253.3 ES6 模块导出与导入写法ES6 模块用export导出用import导入。它支持命名导出和默认导出两种方式。命名导出// src/mjs/details.mjs export const FirstName sachin; export const LastName sahara;导入时用花括号解构// src/mjs/app.mjs import { FirstName, LastName } from ./details.mjs; console.log(FirstName); // sachin默认导出// src/mjs/greeting.mjs export default function greet(name) { console.log(Welcome, name); }导入默认导出不需要花括号// src/mjs/use-greeting.mjs import greet from ./greeting.mjs; greet(Sachin); // Welcome SachinES6 模块还支持export { msg1, msg2 }这种集中导出写法适合一个文件里导出多个变量// src/mjs/messages.mjs const msg1 Hello; const msg2 World; export { msg1, msg2 };导入时同样用解构// src/mjs/use-messages.mjs import { msg1, msg2 } from ./messages.mjs; console.log(msg1, msg2); // Hello World3.4 两种模块系统的核心差异对照对比项CommonJSES6 模块导入语法require()import导出语法module.exports/exportsexport/export default文件扩展名.js默认/.cjs.mjs/.jstypemodule加载时机运行时同步加载编译时静态分析顶层 thismodule.exportsundefined是否支持动态导入原生支持用import()动态导入这张表建议收藏遇到报错时先对照检查自己用的是哪套语法、文件扩展名和package.json配置是否匹配。4. 验证请求跑通两种模块并调用 API4.1 先验证 CommonJS 模块能正常导入导出在项目根目录执行node src/cjs/app.cjs如果输出Hello World!说明 CommonJS 模块配置正确。再执行node src/cjs/use-person.cjs输出Sachin , 20和Welcome Sachin说明属性导出和方法导出都没问题。4.2 再验证 ES6 模块能正常导入导出执行node src/mjs/app.mjs输出sachin说明命名导出和导入正常。再执行node src/mjs/use-greeting.mjs输出Welcome Sachin说明默认导出正常。4.3 在模块中调用 TaoToken API 验证下面写一个 CommonJS 版本的 API 调用模块用来验证 Key 和接口是否可用// src/cjs/ai-client.cjs const https require(https); function chatCompletion(prompt) { return new Promise((resolve, reject) { const data JSON.stringify({ model: gpt-4o-mini, messages: [{ role: user, content: prompt }] }); const options { hostname: taotoken.net, path: /api/v1/chat/completions, method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.TAOTOKEN_API_KEY} } }; const req https.request(options, (res) { let body ; res.on(data, (chunk) body chunk); res.on(end, () resolve(JSON.parse(body))); }); req.on(error, reject); req.write(data); req.end(); }); } module.exports { chatCompletion };调用方式// src/cjs/test-api.cjs require(dotenv).config(); const { chatCompletion } require(./ai-client.cjs); chatCompletion(用一句话解释什么是模块化).then((res) { console.log(res.choices[0].message.content); });执行node src/cjs/test-api.cjs如果返回一段关于模块化的解释说明 API Key 和网络请求都正常。ES6 版本写法类似只是导入导出语法不同// src/mjs/ai-client.mjs export async function chatCompletion(prompt) { const res await fetch(https://taotoken.net/api/v1/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.TAOTOKEN_API_KEY} }, body: JSON.stringify({ model: gpt-4o-mini, messages: [{ role: user, content: prompt }] }) }); return res.json(); }调用// src/mjs/test-api.mjs import dotenv/config; import { chatCompletion } from ./ai-client.mjs; const res await chatCompletion(用一句话解释什么是模块化); console.log(res.choices[0].message.content);注意 ES6 模块里可以直接用顶层await不需要包在 async 函数里。执行node src/mjs/test-api.mjs验证结果。5. 本篇常见报错排查5.1 Cannot use import statement outside a module这个报错的意思是你在一个被 Node.js 当成 CommonJS 的文件里写了import语句。排查步骤第一步检查文件扩展名。如果是.js去看package.json里type字段是不是commonjs或者没写。如果是要么把文件改成.mjs要么把type改成module。第二步检查package.json的位置。Node.js 会从当前文件向上逐级查找最近的package.json如果子目录里有一个package.json覆盖了配置也会导致解析行为不一致。第三步确认没有在 CommonJS 文件里混用import。CommonJS 只能用require()。5.2 require is not defined in ES module scope这个报错反过来你在 ES6 模块里用了require()。ES6 模块没有require、__dirname、__filename这些 CommonJS 特有的变量。解决办法有两种。第一种改用import语法。第二种如果必须用require可以通过createRequire创建// src/mjs/use-require.mjs import { createRequire } from module; const require createRequire(import.meta.url); const pkg require(./package.json); console.log(pkg.name);5.3 ERR_REQUIRE_ESM当你用require()去加载一个 ES6 模块文件时会报这个错。比如require(./details.mjs)就会触发。解决方式要么把被加载的文件改成 CommonJS.cjs要么在 CommonJS 里用动态import()// src/cjs/load-esm.cjs import(./details.mjs).then((mod) { console.log(mod.FirstName); });注意import()返回的是 Promise所以要用.then()或await。5.4 找不到模块路径require(./message)不写扩展名时Node.js 会按.js、.json、.node的顺序查找。但在 ES6 模块里import必须写完整扩展名import ./message会直接报错必须写成import ./message.mjs或import ./message.js。另外导入文件夹时CommonJS 会找index.jsES6 模块不会自动找index.js需要显式写import ./folder/index.mjs。5.5 混用导致的循环依赖问题CommonJS 和 ES6 模块对循环依赖的处理方式不同。CommonJS 在循环依赖时可能拿到不完整的module.exports而 ES6 模块通过静态分析能更好地处理。如果你的项目里两个模块互相导入建议统一用一种模块系统不要混用。6. 继续深入从模块化到实际项目接入模块化配置跑通之后下一步就是把它用到真实项目里。如果你在做 AI 相关的 Node.js 后端建议把 API 调用封装成独立模块CommonJS 和 ES6 各维护一份适配层业务代码只依赖统一的接口。需要长期编码或做 Agent 项目的可以看看 Coding Plan它更适合持续调用模型的场景。接入过程中遇到参数问题可以对照 接入文档 排查。如果你只是想先验证模型返回格式用 模型对话 快速试一下就行。最后提醒一个实际开发中的小技巧在package.json里加一个engines字段锁定 Node.js 版本比如node: 18.0.0因为 ES6 模块的顶层await和fetch在低版本 Node.js 里不可用。这样团队协作时不会因为版本差异导致模块解析行为不一致。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。