1. 模块化发展简史
当我们在2005年前后写JavaScript时,全局变量的污染就像野草丛生的后院。随着前端工程复杂度的指数级增长,开发者们开始思考如何将代码拆分成可维护的单元。这种需求催生了最早的"模块化编程"概念,最终发展出了三种代表性解决方案:AMD、CommonJS和ES modules。
2. CommonJS:Node.js的默认选择
技术栈:Node.js v14+
2.1 基础用法
// mathUtils.js
const PI = 3.1415926
function circleArea(r) {
return PI * r * r
}
// 标准导出语法
module.exports = {
calcArea: circleArea
}
// index.js
const { calcArea } = require('./mathUtils')
console.log(`圆的面积:${calcArea(5)}`) // 输出:78.539815
2.2 动态加载特性
// 运行时动态决定加载模块
const isProduction = process.env.NODE_ENV === 'production'
const logger = isProduction
? require('./simpleLogger')
: require('./detailLogger')
logger.log('系统启动完成')
优点:
- 天然的同步加载机制
- 明确的导出/导入语义
- 成熟的工具链支持
注意事项:
- 循环依赖可能导致意外结果
- 浏览器环境需要打包工具转换
- 默认进行缓存(可能需delete require.cache清理)
3. AMD:浏览器的异步解决方案
技术栈:RequireJS 2.3.6
3.1 基本模块定义
<!-- index.html -->
<script src="require.js" data-main="main"></script>
// main.js
requirejs.config({
paths: {
'jquery': 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min'
}
})
require(['jquery', './mathModule'], function($, math) {
$('#result').text(math.cube(3)) // 输出27
})
// mathModule.js
define(['jquery'], function($) {
function cube(x) {
return x * x * x
}
// 模块返回的值将作为导出对象
return {
cube: cube
}
})
3.2 插件扩展机制
// 加载文本内容插件
require(['text!template.html'], function(tpl) {
document.getElementById('container').innerHTML = tpl
})
最佳实践场景:
- 遗留浏览器项目维护
- 需要动态加载的仪表盘应用
- 多入口SPA应用的按需加载
4. ES Modules:现代标准方案
技术栈:Chrome 90+ / Node.js 14+
4.1 基础实现示例
// geometry.mjs
const TAU = 2 * Math.PI
export function circleCircumference(r) {
return TAU * r
}
// app.mjs
import { circleCircumference } from './geometry.mjs'
console.log(`周长:${circleCircumference(3)}`) // 18.84955592153876
4.2 动态加载模式
// 按需加载大型模块
document.getElementById('mapBtn').addEventListener('click', async () => {
const mapModule = await import('./mapRenderer.js')
mapModule.initMap()
})
4.3 复合用法示例
// logger.mjs
export default class Logger {
constructor(name) {
this.name = name
}
log(message) {
console.log(`[${new Date().toISOString()}] ${this.name}: ${message}`)
}
}
// 统一导出入口
export * from './network.js'
export { default as UserModel } from './user.js'
升级注意事项:
- 需要显式声明.mjs扩展名(或配置type)
- Node.js中需添加--experimental-modules标志(v14)
- 浏览器环境需