JavaScript模块化是用export和import显式声明依赖与接口的代码组织方式,通过语言原生机制实现作用域隔离、静态可分析和tree-shaking;不能直接在全局写函数,否则导致变量覆盖、函数互相干扰、依赖关系混乱;ES Modules需注意路径带后缀、import只能在顶层、不可混用require、命名导出须严格匹配;模块目录应按功能而非技术类型组织,如user/下聚合所有用户相关逻辑;CommonJS与ESM不可混用,须统一模块系统;模块化核心在于每个export被需要、每个import来源明确、每个模块职责单一。
JavaScript模块化是用 export 和 import 显式声明依赖与接口的代码组织方式,不是“把文件拆开就行”,而是通过语言原生机制实现作用域隔离、静态可分析、可 tree-shaking 的结构约束。
传统脚本把所有逻辑堆进 或单个 .js 文件,会导致:
• userName 被多个脚本反复覆盖
• fetchData() 和 saveData() 互相改对方的变量
• 不知道谁在用哪个函数,改一个就崩一片
模块化强制“不暴露就不被访问”——内部变量默认私有,必须靠 export 才能被别人 import。
浏览器和现代 Node.js(v14.3+ 且 package.json 中设 "type": "module")直接支持,但细节极易踩坑:
import { add } fro
m './mathUtils.js' —— 写成 ./mathUtils 会 404import 只能出现在顶层,不能放在 if 或函数里(动态加载要用 import() 函数)require() 和 import:Node.js 中启用了 ESM 后,require 就不可用export const API_URL = '...' → 必须 import { API_URL } from ...,不能 import API_URL
/* utils/date.js */
export function formatDate(date) {
return date.toLocaleDateString('zh-CN');
}
export const DEFAULT_FORMAT = 'YYYY-MM-DD';
/ main.js /
import { formatDate, DEFAULT_FORMAT } from './utils/date.js';
console.log(formatDate(new Date())); // 正确
按功能组织,而不是按技术类型。比如用户相关逻辑全塞进 user/ 目录,而不是分散在 controllers/user.js、models/user.js、views/user.js:
user.service.js 和 user.api.js 却发现它们根本没导出同名函数user/ 目录即可,不会漏掉某个 api/ 下残留的 userHelper.js
典型推荐结构:
src/
├── user/
│ ├── index.js /* 统一导出: export { login, logout } */
│ ├── auth.js /* 只处理登录登出 */
│ └── user.test.js
├── product/
│ ├── index.js
│ ├── list.js
│ └── detail.js
Node.js 项目中常见错误:旧代码用 module.exports,新文件用 export default,结果运行时报 ReferenceError: require is not defined 或 Cannot use import statement outside a module。
"type": "module"),要么全 CommonJS(不设 type,默认)createRequire 临时桥接,但别长期依赖模块化的关键不在“有没有 import”,而在每个 export 是否真的被需要、每个 import 是否明确知道来源、每个模块是否只做一件事且边界清晰——否则只是把混乱从一个文件搬到了十个文件。