当前位置: 首页 > news >正文

Node.js入门教程(二十):模块导入

一、模块系统概述

当你开始写 Node.js 项目时,最先遇到的问题之一就是——如何导入模块(module)。

在 Node.js 里,模块就是可以重复使用的 JavaScript 文件,它们之间通过导入(import)和导出(export)互相通信。

Node.js 生态系统主要支持两种模块系统:

模块系统规范文件扩展名导入方式导出方式
CommonJSNode.js 早期使用.jsrequire()module.exports/exports
ES Module (ESM)ECMAScript 标准.mjs"type": "module"importexport/export default

二、CommonJS 模块系统

CommonJS 是 Node.js 最早也是最广泛使用的模块系统,它采用同步加载的方式,这意味着模块在加载时会阻塞程序的执行,直到加载完成。

导出模块

在 CommonJS 中,使用module.exportsexports对象来导出模块。

// math.js function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } // 方式一:使用 module.exports 导出对象 module.exports = { add, subtract }; // 方式二:使用 exports 逐个导出 // exports.add = add; // exports.subtract = subtract;

导入模块

使用require()函数来导入模块:

// app.js const math = require('./math.js'); console.log(math.add(2, 3)); // 5 console.log(math.subtract(5, 2)); // 3 // 也可以解构导入 const { add, subtract } = require('./math.js'); console.log(add(2, 3)); // 5

module.exports 与 exports 的区别

// user.js const name = 'Alice'; const age = 30; // 方式一:使用 module.exports 导出单个对象 module.exports = { name: name, age: age, sayHello: () => { console.log(`Hello, my name is ${name}.`); } }; // 方式二:使用 exports 导出多个具名变量 // exports 是 module.exports 的一个引用 // exports.name = name; // exports.age = age; // exports.sayHello = () => { // console.log(`Hello, my name is ${name}.`); // };
// main.js const user = require('./user.js'); console.log(user.name); // 输出: Alice user.sayHello(); // 输出: Hello, my name is Alice. // 也可以直接解构 const { name, age } = require('./user.js'); console.log(name); // 输出: Alice

CommonJS 特点

特点说明
同步加载适合服务器端环境,模块通常都在本地文件系统中,加载速度快
运行时加载require()可以在代码的任何位置调用,支持条件动态加载
缓存机制require()加载的模块会被缓存,第二次导入时直接从缓存中读取,避免重复加载

三、ES Module(ESM)规范

ESM 是 JavaScript 官方的模块标准,它采用异步加载,是浏览器和现代 Node.js 应用的首选。

启用 ESM

要在 Node.js 中使用 ESM,有两种方式:

方式一:在package.json中添加"type": "module"

{ "type": "module" }

方式二:将文件扩展名更改为.mjs

导出模块

ESM 支持两种导出方式:具名导出默认导出

// math.mjs // 具名导出 export function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; } export const PI = 3.14159; // 默认导出 export default function multiply(a, b) { return a * b; }

导入模块

// app.mjs // 导入具名导出 import { add, subtract, PI } from './math.mjs'; // 导入默认导出(可以自定义名称) import multiply from './math.mjs'; // 同时导入具名和默认导出 import multiply, { add, subtract } from './math.mjs'; console.log(add(2, 3)); // 5 console.log(subtract(5, 2)); // 3 console.log(PI); // 3.14159 console.log(multiply(2, 3)); // 6 // 导入所有具名导出,作为对象的属性 import * as math from './math.mjs'; console.log(math.add(2, 3)); // 5 console.log(math.default(2, 3)); // 6(默认导出作为 default 属性)

ESM 特点

特点说明
异步加载默认是异步加载,不会阻塞主线程,更适合浏览器环境
静态分析importexport语句在代码执行前就可以确定模块的依赖关系,支持 Tree Shaking 等优化
严格模式ESM 模块默认在严格模式下运行

四、CommonJS 和 ESM 的对比

项目CommonJSESM
语法require/module.exportsimport/export
加载机制运行时同步加载编译时静态加载
默认支持Node.js 默认支持.mjs"type": "module"
适合场景后端脚本、老项目前后端现代项目、Tree-shaking
是否可混用不能直接混用(需额外配置)不能直接混用(需额外配置)

五、混合使用

Node.js 在较新版本中支持两种模块系统共存。可以在同一个项目中同时使用 CommonJS 和 ESM,但需要注意以下几点:

  • ESM 中不能直接使用require()module.exports

  • CommonJS 中不能直接使用importexport

ESM 中导入 CommonJS 模块

ESM 会将 CommonJS 模块视为默认导出:

// commonjs_module.js module.exports = { data: 'hello' };
// esm_module.mjs import commonModule from './commonjs_module.js'; console.log(commonModule.data); // 输出: hello

CommonJS 中导入 ESM 模块

需要使用动态import()函数:

// esm_module.mjs export const name = 'Bob'; export const age = 25;
// commonjs_module.js async function loadESM() { const { name, age } = await import('./esm_module.mjs'); console.log(name); // 输出: Bob console.log(age); // 输出: 25 } loadESM();

六、路径解析规则

相对路径导入

使用./../开头:

// 导入同目录下的模块 const mod = require('./myModule'); // 导入父目录下的模块 const mod = require('../lib/myModule');

绝对路径导入

使用/开头(指向文件系统根目录):

const mod = require('/path/to/module');

核心模块导入

直接使用模块名称:

const fs = require('fs'); const http = require('http');

第三方模块导入

使用模块名称(从node_modules中查找):

const express = require('express'); const lodash = require('lodash');

七、动态导入

CommonJS 中的动态导入

CommonJS 的require()本身就是动态的,可以在任何位置调用:

// 条件导入 if (condition) { const moduleA = require('./moduleA'); moduleA.doSomething(); } else { const moduleB = require('./moduleB'); moduleB.doSomething(); }

ESM 中的动态导入

ESM 使用import()函数进行动态导入,返回一个 Promise:

// 动态导入 async function loadModule(condition) { if (condition) { const moduleA = await import('./moduleA.mjs'); moduleA.doSomething(); } else { const moduleB = await import('./moduleB.mjs'); moduleB.doSomething(); } }

八、常见问题

1. Cannot find module 错误

当 Node.js 找不到模块时,会报Cannot find module错误。检查:

  • 路径是否正确

  • 模块是否已安装(第三方模块)

  • 文件是否存在

2. 循环依赖问题

当两个模块相互引用时,可能导致其中一个模块获取到不完整的导出对象。建议:

  • 避免循环依赖

  • 如果无法避免,使用module.exports提前导出

3. ESM 和 CommonJS 混用问题

  • 在 ESM 中导入 CommonJS 模块时,CommonJS 的module.exports会被视为默认导出

  • 在 CommonJS 中导入 ESM 模块时,必须使用动态import()

  • 文件扩展名很重要:ESM 使用.mjs,CommonJS 使用.js

九、本章小结

知识点说明
CommonJSNode.js 默认模块系统,使用require()/module.exports,同步加载
ES ModuleJavaScript 官方标准,使用import/export,异步加载
启用 ESM使用.mjs扩展名或在package.json中设置"type": "module"
具名导出export const name = ...,导入时需用{ name }
默认导出export default ...,导入时可自定义名称
混用规则ESM 中可导入 CommonJS;CommonJS 中需用import()导入 ESM
路径解析核心模块直接名称、本地模块用./、第三方模块从node_modules查找

最佳实践:对于新的项目,强烈推荐使用 ES Modules (ESM)。它不仅是 JavaScript 的官方标准,而且与现代前端工具链(如 Vite、Next.js)兼容性更好,能够充分利用 Tree Shaking 等优化技术,减少打包后的代码体积。如果你正在维护一个老旧的 CommonJS 项目,并且不需要 ESM 的特性,可以继续使用 CommonJS。如果需要引入新的依赖或利用 ESM 的新特性,可以考虑逐步迁移,或者使用混合模式来过渡。

http://www.jsqmd.com/news/1392126/

相关文章:

  • 东莞大型网站建设公司如何选择?揭秘高品质建站背后的逻辑与避坑指南
  • 基于YOLOv8的AI自瞄完整实战指南:从环境搭建到参数调优
  • 武汉中职 / 中专想学宠物医疗与护理专业推荐学校 —— 武汉市城铁技工学校 - 升学择校早知道
  • Needle 2:14MB 工具调用模型的工程极限与真实边界
  • 沈阳中街附近民宿预算300到500怎么住哪家好?2026夏季实测靠谱关东住宿推荐 - 阿木小鸟
  • Snipe-IT 开源资产管理:10 步 Docker 部署实战,半小时搭好企业 IT 资产台账
  • 技术解析:办公自动化(OA)系统有哪些?按企业规模匹配选型
  • LangChain 和 LangGraph 到底啥关系?一篇讲清怎么选
  • 非人灵长类Th1/Th2及炎症免疫应答的多因子检测:从亚群分化到模型应用
  • 2026合肥哪个单招辅导班比较靠谱?合肥共达校内单招辅导班,保底录取! - 小张zc
  • 2026年湖北电大中专怎么报名?联系方式多少? - 最新资讯
  • 站长之家工具生态 vs SpeedCE:Ping/测速/Whois 分工
  • 2026郑州全屋定制靠谱门店推荐 - 甄选测评馆
  • 语义分割任务
  • 进销存系统为什么总有数据断层?采购单、入库单、销售单如何实现自动关联?
  • Windows更新修复工具终极指南:Reset Windows Update Tool 完整使用与避坑手册
  • 3.PID控制中的微分环节:给PI踩刹车的关键
  • 告别啃生肉:一篇搞定LunaTranslator视觉小说实时翻译的完整指南
  • 电子课本下载工具tchMaterial-parser使用教程:4步把智慧教育平台的课本变成本地PDF
  • 图片转STL一键搞定:把照片变成可3D打印的立体浮雕,新手四步就能出活
  • 华为OD机试 - 智能广播合并台号 - 数组(Java 新系统 100分)
  • 2026外贸CRM软件推荐:AI赋能商机挖掘,业客一体化选型指南 - 优质品牌中立测评推荐
  • 让 Java 直连 Windows 共享:jcifs-ng 从零到一实战指南
  • 樊嘉院士Science|时空多组学
  • 从奇点大会看技术会议的价值,普通开发者怎么选会省钱
  • 第三十三届北京国际广播电影电视展览会(BIRTV2026)即将启幕,劲捷KINGJOY将携六类重磅产品强势亮相 - 甄选测评馆
  • 10 分钟搭建 Windows 专属文件同步:SyncTrayzor 新手完整指南
  • 燕郊医疗网站建设如何打造既专业又具患者温度的线上服务平台
  • 051、ISP Pipeline的“数据流断点“——RAW域到RGB域转换时位宽截断导致的banding伪影排查与修复
  • Windows系统文件spwmp.dll丢失找不到问题解决