JS模块化指的是将复杂的JavaScript代码按照功能、职责拆分成多个独立的模块单元,每个模块拥有独立的作用域,只对外暴露需要共享的变量、函数或对象,其他模块通过规范的语法引入所需的内容。这种代码组织方式可以有效避免全局作用域污染,降低代码的耦合度,提升代码的复用性和可维护性。

为什么需要JS模块化
在早期的前端开发中,所有JS代码都写在一个或多个全局脚本文件里,很容易出现以下问题:
- 全局变量污染:不同脚本中定义同名全局变量会互相覆盖,引发难以排查的bug
- 依赖管理混乱:脚本的加载顺序需要人工控制,一旦顺序出错就会导致代码运行异常
- 代码复用困难:重复的功能逻辑需要多次编写,修改时也要同步修改多个位置
JS模块化的出现就是为了解决这些问题,让代码的组织和管理更规范高效。
常见的JS模块化规范
CommonJS规范
CommonJS是Node.js默认的模块化规范,主要用于服务端JavaScript环境,它的核心是通过require引入模块,通过module.exports或者exports导出模块内容。
下面是一个CommonJS模块的示例:
// math.js 导出模块
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
// 导出两个函数
module.exports = {
add,
subtract
};
// main.js 引入模块
const math = require('./math.js');
console.log(math.add(1, 2)); // 输出3
console.log(math.subtract(5, 3)); // 输出2
ES module规范
ES module是ECMAScript官方推出的模块化规范,现在已经被现代浏览器和Node.js新版本支持,它使用import和export关键字来导入导出模块,是前端开发目前最推荐的模块化方案。
ES module的示例如下:
// utils.js 导出模块
export const PI = 3.1415926;
export function multiply(x, y) {
return x * y;
}
// 也可以默认导出
const greeting = (name) => `Hello, ${name}`;
export default greeting;
// app.js 引入模块
import greet, { PI, multiply } from './utils.js';
console.log(PI); // 输出3.1415926
console.log(multiply(3, 4)); // 输出12
console.log(greet('World')); // 输出Hello, World
AMD规范
AMD(Asynchronous Module Definition)是专门用于浏览器端的异步模块化规范,它的特点是模块加载是异步的,不会阻塞页面渲染,代表实现是RequireJS。不过随着ES module的普及,AMD规范现在已经很少使用了。
JS模块化的核心特点
| 特点 | 说明 |
|---|---|
| 模块作用域 | 每个模块内部的变量、函数默认只在模块内部生效,不会影响全局作用域 |
| 显式依赖 | 模块的依赖关系通过导入语法明确声明,不需要人工控制加载顺序 |
| 按需导出 | 模块可以只导出需要对外暴露的内容,内部实现细节可以隐藏 |
| 静态分析 | ES module的导入导出是静态的,编译阶段就可以确定依赖关系,支持tree shaking优化 |
如何使用JS模块化
在浏览器中使用ES module时,需要在script标签上添加type="module"属性,浏览器才会按照模块化规范解析脚本:
<!-- 入口文件 --> <script type="module" src="./app.js"></script>
如果是Node.js环境,使用CommonJS规范可以直接运行脚本,使用ES module的话需要在package.json中添加"type": "module"配置,或者将模块文件后缀改为.mjs。
注意:不同模块化规范之间的语法不互通,比如CommonJS的
require不能直接在ES module中使用,ES module的import也不能在CommonJS中使用,实际开发中需要根据运行环境选择合适的规范。