JavaScript模块化是解决代码混乱的工程实践,强制功能分文件、限定暴露接口;用export/import而非全局变量可避免覆盖、依赖不清和tree-shaking失效;命名导出需解构导入,默认导出可重命名但每文件仅一个;浏览器ESM需type="module"、带后缀路径、无别名;Node.js中CommonJS与ESM不互通,需注意exports字段和__dirname替代方案;模块边界应按场景拆分而非盲目细化。

JavaScript模块化开发不是一种新语法糖,而是解决“代码越写越乱、改一处崩一片”的工程实践——它强制你把功能切分成有边界的文件,每个文件只暴露别人真正需要的东西。
为什么必须用 export 和 import,而不是直接挂全局变量?
挂 window.utils = {...} 看似简单,但会立刻引发三类问题:
• 其他脚本可能意外覆盖同名变量,比如两个库都定义了 log;
• 无法知道谁依赖了谁,重构时不敢删函数;
• 打包工具没法做 tree-shaking(自动剔除没用的代码),最终 JS 包体积虚胖。
export 的两种写法,选错就埋坑
命名导出和默认导出行为完全不同,混用极易导致导入失败:
- 命名导出(适合导出多个工具):
export const add = (a, b) => a + b;→ 必须用import { add } from './math.js',名字错一个就undefined - 默认导出(适合导出模块主入口):
export default class ApiClient { ... }→ 可用任意名导入:import Client from './api.js',但一个文件只能有一个export default - 禁止这样写:
export default function(){}; export const PI = 3.14;虽然合法,但会让团队成员不确定该用哪种方式导入,TypeScript 类型推断也会变弱
浏览器里跑 import,三个硬性条件缺一不可
不是写了 import 就能运行,浏览器原生 ESM 有严格限制:
-
:HTML 中引入入口脚本时必须加这个属性,否则报Cannot use import statement outside a module - 路径必须带后缀:
import { foo } from './utils.js'(不能写成./utils) - 不能用相对路径别名:
import { bar } from '@lib/config'在浏览器中直接报 404,需靠 Vite/Webpack 等工具转译
Node.js 中模块系统更复杂,别想当然混用
Node.js 同时支持 CommonJS(require/module.exports)和 ESM(import/export),但二者不完全互通:
立即学习“Java免费学习笔记(深入)”;
- 在
type: "module"的项目里,require('./x')直接报错;反过来,在 CommonJS 里import也不行 -
__dirname和__filename在 ESM 中不存在,要用import.meta.url配合fileURLToPath() - 第三方包若同时提供
exports字段(如"exports": { ".": { "import": "./dist/index.mjs" } }),Node.js 才能正确选择 ESM 版本;否则可能加载到 CommonJS 版本导致default导入为undefined
utils 模块导出 20 个函数,远不如按场景拆成 dateUtils.js、stringUtils.js 并各自控制导出粒度来得可靠。











