Appearance
JavaScript 模块系统
模块系统是现代JavaScript开发的核心特性,它允许我们将代码分割成可重用的块,提高代码的可维护性和可组织性。
模块基础概念
模块是一个包含JavaScript代码的文件,它有自己的作用域,不会污染全局作用域。模块可以导出值、函数、类等,也可以导入其他模块导出的内容。
模块的特性
javascript
// 模块默认在严格模式下运行
// 在模块中,顶层的this是undefined,而不是window
// 模块只执行一次,结果会被缓存
// 无论导入多少次,模块只会被初始化一次
基本导出 (Named Exports)
javascript
// math.js - 命名导出
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// 也可以在末尾导出
function subtract(a, b) {
return a - b;
}
function divide(a, b) {
if (b === 0) throw new Error('除数不能为零');
return a / b;
}
export { subtract, divide };
基本导入
javascript
// main.js - 导入命名导出
import { PI, add, multiply } from './math.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
console.log(multiply(4, 5)); // 20
重命名导入/导出
javascript
// geometry.js
export const CIRCLE = 'circle';
export const SQUARE = 'square';
// main.js - 重命名导入
import { CIRCLE as CircleType, SQUARE as SquareType } from './geometry.js';
console.log(CircleType); // 'circle'
console.log(SquareType); // 'square'
// 重命名导出
export { CIRCLE as ROUND_SHAPE } from './geometry.js';
默认导出 (Default Export)
javascript
// calculator.js - 默认导出
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
multiply(a, b) {
return a * b;
}
divide(a, b) {
if (b === 0) throw new Error('除数不能为零');
return a / b;
}
}
export default Calculator;
// 或者默认导出函数
export default function calculate(operation, a, b) {
switch (operation) {
case 'add': return a + b;
case 'subtract': return a - b;
case 'multiply': return a * b;
case 'divide': return a / b;
default: throw new Error('未知操作');
}
}
默认导入
javascript
// main.js - 导入默认导出
import Calculator from './calculator.js';
// 或者给默认导出起任意名称
import MyCalculator from './calculator.js';
const calc = new Calculator();
console.log(calc.add(5, 3)); // 8
混合导入/导出
javascript
// shapes.js - 混合导出
export const CIRCLE = 'circle';
export const SQUARE = 'square';
class Shape {
constructor(type) {
this.type = type;
}
}
export default Shape;
// main.js - 混合导入
import Shape, { CIRCLE, SQUARE } from './shapes.js';
const circle = new Shape(CIRCLE);
const square = new Shape(SQUARE);
整体导入
javascript
// main.js - 整体导入所有导出
import * as MathUtils from './math.js';
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(2, 3)); // 5
console.log(MathUtils.multiply(4, 5)); // 20
导入时重命名
javascript
// main.js - 导入时重命名
import {
add as addNumbers,
multiply as multiplyNumbers
} from './math.js';
console.log(addNumbers(2, 3)); // 5
console.log(multiplyNumbers(4, 5)); // 20
仅导入副作用
javascript
// styles.css.js - 只导入模块的副作用(如样式注入)
import './styles.css';
// polyfill.js - 只导入polyfill的副作用
import 'babel-polyfill';
动态导入
ES2020引入了动态导入,允许在需要时异步加载模块。
基本动态导入
javascript
// 动态导入返回Promise
async function loadModule() {
const { add, multiply } = await import('./math.js');
return add(multiply(2, 3), 1); // (2 * 3) + 1 = 7
}
// 条件导入
async function loadFeature() {
if (window.location.search.includes('advanced=true')) {
const { AdvancedFeature } = await import('./advanced.js');
return new AdvancedFeature();
} else {
const { BasicFeature } = await import('./basic.js');
return new BasicFeature();
}
}
// 按需导入
document.getElementById('loadButton').addEventListener('click', async () => {
const { renderChart } = await import('./chart.js');
renderChart();
});
动态导入的实际应用
javascript
// 路由级别的代码分割
async function loadRoute(routeName) {
try {
const moduleMap = {
'home': './routes/Home.js',
'about': './routes/About.js',
'contact': './routes/Contact.js'
};
const modulePath = moduleMap[routeName];
if (!modulePath) {
throw new Error(`未知路由: ${routeName}`);
}
const routeModule = await import(modulePath);
return routeModule.default;
} catch (error) {
console.error('路由加载失败:', error);
// 加载错误页面
const errorModule = await import('./routes/Error.js');
return errorModule.default;
}
}
// 国际化动态导入
async function loadLocale(locale) {
try {
const translations = await import(`./locales/${locale}.js`);
return translations.default;
} catch (error) {
console.warn(`本地化文件 ${locale} 加载失败,回退到默认语言`);
const defaultTranslations = await import('./locales/en.js');
return defaultTranslations.default;
}
}
CommonJS (Node.js)
CommonJS是Node.js早期使用的模块系统,使用require和module.exports。
CommonJS 基础
javascript
// math.js (CommonJS)
const PI = 3.14159;
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
// 导出方式1:逐个导出
exports.PI = PI;
exports.add = add;
exports.multiply = multiply;
// 导出方式2:整体导出
module.exports = {
PI,
add,
multiply,
subtract: (a, b) => a - b
};
// 导出方式3:默认导出
module.exports = class Calculator {
add(a, b) {
return a + b;
}
};
CommonJS 导入
javascript
// main.js (CommonJS)
const { PI, add, multiply } = require('./math.js');
const Calculator = require('./calculator.js');
// 整体导入
const math = require('./math.js');
模块加载器和打包工具
浏览器中的模块
html
<!-- 在HTML中使用模块 -->
<script type="module" src="./main.js"></script>
<!-- 内联模块脚本 -->
<script type="module">
import { add } from './math.js';
console.log(add(2, 3));
</script>
模块路径解析
javascript
// 相对路径
import { helper } from './utils.js';
import { config } from '../config/app.js';
// 绝对路径(相对于基URL)
import { api } from '/src/services/api.js';
// 完整URL
import { library } from 'https://cdn.skypack.dev/lodash';
// Node.js内置模块(在支持的环境中)
import fs from 'fs';
import path from 'path';
高级模块模式
模块工厂模式
javascript
// moduleFactory.js
function createModule(config) {
return {
name: config.name,
dependencies: config.dependencies || [],
async initialize() {
// 加载依赖
const dependencyModules = await Promise.all(
this.dependencies.map(dep => import(dep))
);
// 初始化逻辑
return this;
},
getExports() {
return {
name: this.name,
// 其他导出
};
}
};
}
export default createModule;
// 使用模块工厂
const myModule = createModule({
name: 'MyModule',
dependencies: ['./dependency1.js', './dependency2.js']
});
模块装饰器模式
javascript
// decorator.js
export function withLogging(modulePath) {
return async function() {
console.log(`正在加载模块: ${modulePath}`);
const module = await import(modulePath);
console.log(`模块加载完成: ${modulePath}`);
return module;
};
}
// 使用装饰器
const { add } = await withLogging('./math.js')();
条件导出
javascript
// package.json 中的条件导出配置
// {
// "name": "my-package",
// "exports": {
// ".": {
// "import": "./dist/index.mjs",
// "require": "./dist/index.cjs",
// "default": "./index.mjs"
// }
// }
// }
// 在模块内部根据环境条件导出
let exportedModule;
if (typeof window !== 'undefined') {
// 浏览器环境
exportedModule = { ...browserAPI };
} else {
// Node.js环境
exportedModule = { ...nodeAPI };
}
export default exportedModule;
模块最佳实践
模块设计原则
javascript
// 1. 单一职责原则 - 每个模块只负责一个功能
// user.js - 用户相关功能
export class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
export function validateUser(user) {
return user.name && user.email;
}
// 2. 明确的接口 - 清晰地导出需要的API
// api.js
export const API_BASE_URL = 'https://api.example.com';
export async function fetchUser(id) {
// 实现
}
export async function updateUser(user) {
// 实现
}
// 不要导出内部实现细节
// export function _internalHelper() { ... } // 避免这样做
模块性能优化
javascript
// 1. 懒加载 - 按需导入
async function initializeChart() {
// 只在需要时加载图表库
const { Chart } = await import('chart.js');
return new Chart();
}
// 2. 代码分割 - 将不常用的代码分离
async function loadAdminFeatures() {
if (user.role === 'admin') {
const adminModule = await import('./admin-panel.js');
return adminModule;
}
}
// 3. 预加载重要的模块
// 在应用启动时预加载
const essentialModules = Promise.all([
import('./router.js'),
import('./auth.js'),
import('./api.js')
]);
// 使用时等待预加载完成
async function initializeApp() {
const [router, auth, api] = await essentialModules;
// 继续初始化
}
模块测试
javascript
// calculator.js
export class Calculator {
add(a, b) {
return a + b;
}
multiply(a, b) {
return a * b;
}
}
// calculator.test.js
import { Calculator } from './calculator.js';
describe('Calculator', () => {
let calc;
beforeEach(() => {
calc = new Calculator();
});
test('add should return sum of two numbers', () => {
expect(calc.add(2, 3)).toBe(5);
});
test('multiply should return product of two numbers', () => {
expect(calc.multiply(2, 3)).toBe(6);
});
});
模块文档化
javascript
/**
* 数学运算工具模块
* 提供基本的数学运算功能
* @module math
*/
/**
* 加法运算
* @param {number} a - 第一个加数
* @param {number} b - 第二个加数
* @returns {number} 两数之和
*/
export function add(a, b) {
return a + b;
}
/**
* 乘法运算
* @param {number} a - 被乘数
* @param {number} b - 乘数
* @returns {number} 两数之积
*/
export function multiply(a, b) {
return a * b;
}
模块生态系统
npm 包的模块化
javascript
// 使用第三方包
import _ from 'lodash';
import { debounce } from 'lodash-es';
// 或者按需导入
import debounce from 'lodash/debounce';
// 创建可发布的模块
// index.js
export { default as Component } from './Component.js';
export { utils } from './utils.js';
export { constants } from './constants.js';
模块兼容性处理
javascript
// 处理ESM和CommonJS的兼容性
let createLogger;
if (typeof module !== 'undefined' && module.exports) {
// CommonJS环境
createLogger = require('./logger.cjs').default;
} else {
// ESM环境
import('./logger.mjs').then(module => {
createLogger = module.default;
});
}
// 或者使用动态导入处理兼容性
async function loadLogger() {
if (typeof window !== 'undefined') {
// 浏览器环境
return await import('./logger.browser.js');
} else {
// Node.js环境
return await import('./logger.node.js');
}
}
JavaScript模块系统提供了强大的代码组织能力,正确使用模块系统可以让代码更加模块化、可维护和可重用。