模块化概述
什么是模块化
模块化是一种将代码分解为独立、可重用组件的编程方法。每个模块都有自己的作用域,可以导出特定的功能供其他模块使用。
// 模块化的优势:
// 1. 代码组织:将相关功能组织在一起
// 2. 作用域隔离:避免全局变量污染
// 3. 依赖管理:明确模块间的依赖关系
// 4. 代码复用:模块可以在多个地方使用
// 5. 维护性:便于代码的维护和更新
// 6. 测试:模块可以独立测试
// 模块化发展历程:
// 1. 全局变量时代(无模块化)
// 2. IIFE(立即执行函数表达式)
// 3. CommonJS(Node.js)
// 4. AMD(异步模块定义)
// 5. UMD(通用模块定义)
// 6. ES6 Modules(现代标准)
// 早期的全局变量方式(问题很多)
var utils = {
add: function(a, b) {
return a + b;
},
multiply: function(a, b) {
return a * b;
}
};
// 全局变量的问题
// 1. 命名冲突
// 2. 依赖关系不明确
// 3. 难以维护
// 4. 无法控制访问权限
IIFE模块模式
// IIFE(Immediately Invoked Function Expression)模块模式
// 解决了作用域隔离问题
// 基本IIFE模块
var MathUtils = (function() {
// 私有变量和函数
var PI = 3.14159;
function validateNumber(num) {
return typeof num === 'number' && !isNaN(num);
}
// 公共API
return {
add: function(a, b) {
if (!validateNumber(a) || !validateNumber(b)) {
throw new Error('参数必须是数字');
}
return a + b;
},
multiply: function(a, b) {
if (!validateNumber(a) || !validateNumber(b)) {
throw new Error('参数必须是数字');
}
return a * b;
},
circleArea: function(radius) {
if (!validateNumber(radius) || radius < 0) {
throw new Error('半径必须是非负数字');
}
return PI * radius * radius;
},
// 获取PI值(只读)
getPI: function() {
return PI;
}
};
})();
// 使用IIFE模块
console.log('加法:', MathUtils.add(5, 3));
console.log('乘法:', MathUtils.multiply(4, 7));
console.log('圆面积:', MathUtils.circleArea(5));
console.log('PI值:', MathUtils.getPI());
// 无法访问私有变量
// console.log(PI); // 错误:PI未定义
// console.log(MathUtils.PI); // undefined
// 带参数的IIFE模块(依赖注入)
var StringUtils = (function($) {
// 依赖jQuery(如果可用)
var hasJQuery = typeof $ !== 'undefined';
return {
capitalize: function(str) {
if (typeof str !== 'string') return '';
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
},
truncate: function(str, length, suffix) {
if (typeof str !== 'string') return '';
suffix = suffix || '...';
if (str.length <= length) return str;
return str.slice(0, length - suffix.length) + suffix;
},
// 如果有jQuery,使用jQuery的方法
escapeHtml: function(str) {
if (hasJQuery && $.fn) {
return $('<div>').text(str).html();
}
// 原生实现
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
};
})(typeof jQuery !== 'undefined' ? jQuery : undefined);
// 使用StringUtils
console.log('首字母大写:', StringUtils.capitalize('hello world'));
console.log('截断文本:', StringUtils.truncate('这是一个很长的文本内容', 10));
console.log('HTML转义:', StringUtils.escapeHtml('<script>alert("XSS")</script>'));
// 模块扩展模式
var MathUtils = (function(module) {
// 扩展现有模块
module.subtract = function(a, b) {
return a - b;
};
module.divide = function(a, b) {
if (b === 0) {
throw new Error('除数不能为零');
}
return a / b;
};
return module;
})(MathUtils || {});
console.log('减法:', MathUtils.subtract(10, 3));
console.log('除法:', MathUtils.divide(15, 3));
// 子模块模式
var App = (function() {
var modules = {};
return {
// 注册模块
module: function(name, definition) {
if (typeof definition === 'function') {
modules[name] = definition();
} else {
modules[name] = definition;
}
return this;
},
// 获取模块
getModule: function(name) {
return modules[name];
},
// 获取所有模块
getModules: function() {
return Object.keys(modules);
}
};
})();
// 注册模块
App.module('user', function() {
var users = [];
return {
add: function(user) {
users.push(user);
},
getAll: function() {
return users.slice(); // 返回副本
},
findById: function(id) {
return users.find(user => user.id === id);
}
};
});
App.module('logger', {
log: function(message) {
console.log(`[${new Date().toISOString()}] ${message}`);
},
error: function(message) {
console.error(`[${new Date().toISOString()}] ERROR: ${message}`);
}
});
// 使用子模块
const userModule = App.getModule('user');
const logger = App.getModule('logger');
userModule.add({ id: 1, name: '张三' });
userModule.add({ id: 2, name: '李四' });
logger.log('用户模块测试');
console.log('所有用户:', userModule.getAll());
console.log('查找用户:', userModule.findById(1));
console.log('已注册的模块:', App.getModules());
CommonJS模块
Node.js中的CommonJS
// CommonJS是Node.js使用的模块系统
// 特点:
// 1. 同步加载
// 2. 运行时加载
// 3. 值的拷贝
// 4. 单例模式
// math.js - 导出模块
const PI = 3.14159;
// 私有函数
function validateNumber(num) {
return typeof num === 'number' && !isNaN(num);
}
// 导出单个函数
function add(a, b) {
if (!validateNumber(a) || !validateNumber(b)) {
throw new Error('参数必须是数字');
}
return a + b;
}
// 导出对象
const calculator = {
multiply(a, b) {
if (!validateNumber(a) || !validateNumber(b)) {
throw new Error('参数必须是数字');
}
return a * b;
},
divide(a, b) {
if (!validateNumber(a) || !validateNumber(b)) {
throw new Error('参数必须是数字');
}
if (b === 0) {
throw new Error('除数不能为零');
}
return a / b;
}
};
// 导出类
class Circle {
constructor(radius) {
if (!validateNumber(radius) || radius < 0) {
throw new Error('半径必须是非负数字');
}
this.radius = radius;
}
area() {
return PI * this.radius * this.radius;
}
circumference() {
return 2 * PI * this.radius;
}
}
// 多种导出方式
module.exports = {
add,
calculator,
Circle,
PI: () => PI // 只读访问
};
// 或者分别导出
// exports.add = add;
// exports.calculator = calculator;
// exports.Circle = Circle;
// exports.getPI = () => PI;
// 注意:不能直接赋值给exports
// exports = { add }; // 错误!这样不会导出任何内容
// app.js - 导入模块
const math = require('./math'); // 导入整个模块
const { add, Circle } = require('./math'); // 解构导入
// 使用导入的模块
console.log('加法:', math.add(5, 3));
console.log('乘法:', math.calculator.multiply(4, 7));
const circle = new math.Circle(5);
console.log('圆面积:', circle.area());
console.log('圆周长:', circle.circumference());
// 使用解构导入
console.log('解构导入加法:', add(10, 20));
const smallCircle = new Circle(3);
console.log('小圆面积:', smallCircle.area());
// 模块缓存演示
// counter.js
let count = 0;
module.exports = {
increment() {
return ++count;
},
decrement() {
return --count;
},
getCount() {
return count;
}
};
// main.js
const counter1 = require('./counter');
const counter2 = require('./counter');
console.log('counter1 === counter2:', counter1 === counter2); // true
console.log('初始计数:', counter1.getCount()); // 0
counter1.increment();
console.log('counter1增加后:', counter1.getCount()); // 1
console.log('counter2的计数:', counter2.getCount()); // 1(共享状态)
// 条件加载
function loadModule(moduleName) {
try {
return require(moduleName);
} catch (error) {
console.error(`无法加载模块 ${moduleName}:`, error.message);
return null;
}
}
// 动态加载
const moduleName = process.env.NODE_ENV === 'development' ? './dev-config' : './prod-config';
const config = loadModule(moduleName);
// 模块路径解析
// require('./math') - 相对路径
// require('/abs/path/math') - 绝对路径
// require('lodash') - node_modules中的包
// require('fs') - 内置模块
// 查看模块解析路径
console.log('模块解析路径:', require.resolve('./math'));
console.log('require.cache keys:', Object.keys(require.cache));
// 清除模块缓存(谨慎使用)
function clearModuleCache(modulePath) {
const resolvedPath = require.resolve(modulePath);
delete require.cache[resolvedPath];
}
// 模块包装函数
// Node.js实际上将每个模块包装在一个函数中:
// (function(exports, require, module, __filename, __dirname) {
// // 模块代码
// });
console.log('当前文件名:', __filename);
console.log('当前目录:', __dirname);
console.log('模块对象:', module);
CommonJS模块工具
// module-loader.js - 高级模块加载器
class ModuleLoader {
constructor() {
this.cache = new Map();
this.loading = new Map();
}
// 异步加载模块(模拟)
async load(modulePath) {
if (this.cache.has(modulePath)) {
return this.cache.get(modulePath);
}
if (this.loading.has(modulePath)) {
return this.loading.get(modulePath);
}
const loadPromise = this.loadModule(modulePath);
this.loading.set(modulePath, loadPromise);
try {
const module = await loadPromise;
this.cache.set(modulePath, module);
this.loading.delete(modulePath);
return module;
} catch (error) {
this.loading.delete(modulePath);
throw error;
}
}
async loadModule(modulePath) {
// 模拟异步加载
return new Promise((resolve, reject) => {
setTimeout(() => {
try {
const module = require(modulePath);
resolve(module);
} catch (error) {
reject(error);
}
}, Math.random() * 100);
});
}
// 预加载模块
async preload(modulePaths) {
const promises = modulePaths.map(path => this.load(path));
return Promise.all(promises);
}
// 清除缓存
clearCache(modulePath) {
if (modulePath) {
this.cache.delete(modulePath);
} else {
this.cache.clear();
}
}
// 获取缓存信息
getCacheInfo() {
return {
cached: Array.from(this.cache.keys()),
loading: Array.from(this.loading.keys()),
cacheSize: this.cache.size
};
}
}
// 使用模块加载器
const loader = new ModuleLoader();
// 异步加载示例
(async () => {
try {
const math = await loader.load('./math');
console.log('异步加载的数学模块:', math.add(1, 2));
// 预加载多个模块
await loader.preload(['./math', './counter']);
console.log('预加载完成');
console.log('缓存信息:', loader.getCacheInfo());
} catch (error) {
console.error('模块加载失败:', error);
}
})();
// 模块依赖分析器
class DependencyAnalyzer {
constructor() {
this.dependencies = new Map();
this.visited = new Set();
}
analyze(modulePath) {
if (this.visited.has(modulePath)) {
return this.dependencies.get(modulePath) || [];
}
this.visited.add(modulePath);
try {
const fs = require('fs');
const path = require('path');
const content = fs.readFileSync(modulePath, 'utf8');
const deps = this.extractDependencies(content);
this.dependencies.set(modulePath, deps);
// 递归分析依赖
deps.forEach(dep => {
if (dep.startsWith('./') || dep.startsWith('../')) {
const depPath = path.resolve(path.dirname(modulePath), dep + '.js');
if (fs.existsSync(depPath)) {
this.analyze(depPath);
}
}
});
return deps;
} catch (error) {
console.error(`分析模块 ${modulePath} 失败:`, error.message);
return [];
}
}
extractDependencies(content) {
const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
const dependencies = [];
let match;
while ((match = requireRegex.exec(content)) !== null) {
dependencies.push(match[1]);
}
return [...new Set(dependencies)]; // 去重
}
getDependencyTree() {
const tree = {};
for (const [module, deps] of this.dependencies) {
tree[module] = deps;
}
return tree;
}
findCircularDependencies() {
const circular = [];
const visiting = new Set();
const visited = new Set();
const visit = (module, path = []) => {
if (visiting.has(module)) {
const cycleStart = path.indexOf(module);
circular.push(path.slice(cycleStart).concat(module));
return;
}
if (visited.has(module)) {
return;
}
visiting.add(module);
const deps = this.dependencies.get(module) || [];
deps.forEach(dep => {
visit(dep, path.concat(module));
});
visiting.delete(module);
visited.add(module);
};
for (const module of this.dependencies.keys()) {
if (!visited.has(module)) {
visit(module);
}
}
return circular;
}
}
// 使用依赖分析器
const analyzer = new DependencyAnalyzer();
// analyzer.analyze('./app.js');
// console.log('依赖树:', analyzer.getDependencyTree());
// console.log('循环依赖:', analyzer.findCircularDependencies());
ES6模块
ES6模块语法
// ES6模块是JavaScript的官方模块标准
// 特点:
// 1. 静态结构(编译时确定)
// 2. 异步加载
// 3. 值的引用
// 4. 严格模式
// 5. 顶层this为undefined
// math-es6.js - ES6模块导出
// 命名导出
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 };
// 重命名导出
function power(base, exponent) {
return Math.pow(base, exponent);
}
export { power as pow };
// 默认导出
export default class Calculator {
constructor() {
this.history = [];
}
calculate(operation, a, b) {
let result;
switch (operation) {
case 'add':
result = add(a, b);
break;
case 'subtract':
result = subtract(a, b);
break;
case 'multiply':
result = multiply(a, b);
break;
case 'divide':
result = divide(a, b);
break;
case 'power':
result = power(a, b);
break;
default:
throw new Error(`不支持的操作: ${operation}`);
}
this.history.push({ operation, a, b, result, timestamp: Date.now() });
return result;
}
getHistory() {
return [...this.history];
}
clearHistory() {
this.history = [];
}
}
// 条件导出
if (typeof window !== 'undefined') {
// 浏览器环境
export const environment = 'browser';
} else {
// Node.js环境
export const environment = 'node';
}
// app-es6.js - ES6模块导入
// 导入默认导出
import Calculator from './math-es6.js';
// 导入命名导出
import { add, multiply, PI } from './math-es6.js';
// 导入重命名
import { pow as power } from './math-es6.js';
// 导入所有命名导出
import * as MathUtils from './math-es6.js';
// 混合导入
import Calculator, { add, subtract } from './math-es6.js';
// 动态导入(ES2020)
async function loadMathModule() {
try {
const mathModule = await import('./math-es6.js');
console.log('动态导入的模块:', mathModule);
const calc = new mathModule.default();
console.log('动态计算:', calc.calculate('add', 5, 3));
} catch (error) {
console.error('动态导入失败:', error);
}
}
// 条件动态导入
async function loadModuleConditionally(condition) {
if (condition) {
const { add, multiply } = await import('./math-es6.js');
return { add, multiply };
} else {
const { subtract, divide } = await import('./math-es6.js');
return { subtract, divide };
}
}
// 使用导入的模块
const calculator = new Calculator();
console.log('ES6计算器:', calculator.calculate('add', 10, 5));
console.log('直接使用函数:', add(3, 7));
console.log('使用命名空间:', MathUtils.multiply(4, 6));
console.log('PI值:', PI);
// 模块的值是引用,不是拷贝
// counter-es6.js
let count = 0;
export function increment() {
return ++count;
}
export function getCount() {
return count;
}
export { count }; // 导出变量的引用
// main-es6.js
import { increment, getCount, count } from './counter-es6.js';
console.log('初始计数:', count); // 0
increment();
console.log('增加后计数:', count); // 仍然是0(导入时的值)
console.log('通过函数获取:', getCount()); // 1
// 注意:不能修改导入的变量
// count = 10; // 错误:Assignment to constant variable
ES6模块高级特性
// 模块聚合和重新导出
// utils/index.js - 聚合模块
// 重新导出其他模块的内容
export { add, subtract, multiply, divide } from './math.js';
export { capitalize, truncate } from './string.js';
export { debounce, throttle } from './function.js';
// 重新导出并重命名
export { default as MathCalculator } from './calculator.js';
export { Logger as AppLogger } from './logger.js';
// 重新导出所有内容
export * from './array.js';
export * from './object.js';
// 重新导出并添加新功能
export { format as formatDate } from './date.js';
export function getCurrentDate() {
return new Date().toISOString().split('T')[0];
}
// 条件重新导出
if (process.env.NODE_ENV === 'development') {
export { debug } from './debug.js';
}
// 模块工厂模式
// module-factory.js
export function createModule(config) {
return {
name: config.name,
version: config.version,
init() {
console.log(`模块 ${this.name} v${this.version} 已初始化`);
},
destroy() {
console.log(`模块 ${this.name} 已销毁`);
}
};
}
export class ModuleManager {
constructor() {
this.modules = new Map();
}
register(name, moduleFactory, config) {
if (this.modules.has(name)) {
throw new Error(`模块 ${name} 已存在`);
}
const module = moduleFactory(config);
this.modules.set(name, module);
return module;
}
get(name) {
return this.modules.get(name);
}
unregister(name) {
const module = this.modules.get(name);
if (module && typeof module.destroy === 'function') {
module.destroy();
}
return this.modules.delete(name);
}
list() {
return Array.from(this.modules.keys());
}
}
// 使用模块工厂
import { createModule, ModuleManager } from './module-factory.js';
const manager = new ModuleManager();
manager.register('auth', createModule, {
name: 'Authentication',
version: '1.0.0'
});
manager.register('logger', createModule, {
name: 'Logger',
version: '2.1.0'
});
const authModule = manager.get('auth');
authModule.init();
console.log('已注册的模块:', manager.list());
// 异步模块加载器
class AsyncModuleLoader {
constructor() {
this.cache = new Map();
this.loading = new Map();
}
async load(modulePath) {
// 检查缓存
if (this.cache.has(modulePath)) {
return this.cache.get(modulePath);
}
// 检查是否正在加载
if (this.loading.has(modulePath)) {
return this.loading.get(modulePath);
}
// 开始加载
const loadPromise = this.loadModule(modulePath);
this.loading.set(modulePath, loadPromise);
try {
const module = await loadPromise;
this.cache.set(modulePath, module);
this.loading.delete(modulePath);
return module;
} catch (error) {
this.loading.delete(modulePath);
throw error;
}
}
async loadModule(modulePath) {
try {
const module = await import(modulePath);
return module;
} catch (error) {
throw new Error(`无法加载模块 ${modulePath}: ${error.message}`);
}
}
async loadMultiple(modulePaths) {
const promises = modulePaths.map(path => this.load(path));
return Promise.all(promises);
}
async loadWithFallback(primaryPath, fallbackPath) {
try {
return await this.load(primaryPath);
} catch (error) {
console.warn(`主模块加载失败,使用备用模块: ${error.message}`);
return this.load(fallbackPath);
}
}
preload(modulePaths) {
// 预加载但不等待完成
modulePaths.forEach(path => {
this.load(path).catch(error => {
console.warn(`预加载模块 ${path} 失败:`, error.message);
});
});
}
clearCache(modulePath) {
if (modulePath) {
this.cache.delete(modulePath);
} else {
this.cache.clear();
}
}
getCacheInfo() {
return {
cached: Array.from(this.cache.keys()),
loading: Array.from(this.loading.keys()),
cacheSize: this.cache.size
};
}
}
// 使用异步模块加载器
const loader = new AsyncModuleLoader();
// 预加载常用模块
loader.preload([
'./utils/math.js',
'./utils/string.js',
'./utils/array.js'
]);
// 按需加载
async function handleUserAction(action) {
switch (action) {
case 'calculate':
const mathModule = await loader.load('./utils/math.js');
return mathModule.add(1, 2);
case 'format':
const stringModule = await loader.load('./utils/string.js');
return stringModule.capitalize('hello world');
default:
throw new Error(`未知操作: ${action}`);
}
}
// 模块依赖注入
class DependencyInjector {
constructor() {
this.dependencies = new Map();
this.singletons = new Map();
}
// 注册依赖
register(name, factory, options = {}) {
this.dependencies.set(name, {
factory,
singleton: options.singleton || false,
dependencies: options.dependencies || []
});
}
// 解析依赖
async resolve(name) {
const dependency = this.dependencies.get(name);
if (!dependency) {
throw new Error(`依赖 ${name} 未注册`);
}
// 检查单例缓存
if (dependency.singleton && this.singletons.has(name)) {
return this.singletons.get(name);
}
// 解析依赖的依赖
const resolvedDeps = await Promise.all(
dependency.dependencies.map(dep => this.resolve(dep))
);
// 创建实例
const instance = await dependency.factory(...resolvedDeps);
// 缓存单例
if (dependency.singleton) {
this.singletons.set(name, instance);
}
return instance;
}
// 批量解析
async resolveAll(names) {
const promises = names.map(name => this.resolve(name));
return Promise.all(promises);
}
}
// 使用依赖注入
const injector = new DependencyInjector();
// 注册依赖
injector.register('logger', async () => {
const { Logger } = await import('./logger.js');
return new Logger();
}, { singleton: true });
injector.register('database', async (logger) => {
const { Database } = await import('./database.js');
return new Database(logger);
}, { dependencies: ['logger'] });
injector.register('userService', async (database, logger) => {
const { UserService } = await import('./user-service.js');
return new UserService(database, logger);
}, { dependencies: ['database', 'logger'] });
// 解析并使用
(async () => {
try {
const userService = await injector.resolve('userService');
console.log('用户服务已就绪:', userService);
} catch (error) {
console.error('依赖解析失败:', error);
}
})();
模块打包工具
Webpack基础配置
// webpack.config.js - Webpack配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
// 入口文件
entry: {
main: './src/index.js',
vendor: './src/vendor.js'
},
// 输出配置
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
clean: true // 清理输出目录
},
// 模式
mode: process.env.NODE_ENV || 'development',
// 开发服务器
devServer: {
contentBase: './dist',
hot: true,
port: 3000
},
// 模块解析
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
'@': path.resolve(__dirname, 'src'),
'utils': path.resolve(__dirname, 'src/utils')
}
},
// 模块规则
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: [
process.env.NODE_ENV === 'production'
? MiniCssExtractPlugin.loader
: 'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}
]
},
// 插件
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
// 优化
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
// package.json - 脚本配置
{
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack serve --mode=development",
"analyze": "webpack-bundle-analyzer dist/main.*.js"
},
"devDependencies": {
"webpack": "^5.0.0",
"webpack-cli": "^4.0.0",
"webpack-dev-server": "^4.0.0",
"babel-loader": "^8.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"css-loader": "^6.0.0",
"style-loader": "^3.0.0",
"mini-css-extract-plugin": "^2.0.0",
"html-webpack-plugin": "^5.0.0"
}
}
Rollup配置
// rollup.config.js - Rollup配置
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import terser from '@rollup/plugin-terser';
import { defineConfig } from 'rollup';
export default defineConfig({
input: 'src/index.js',
output: [
{
file: 'dist/bundle.cjs.js',
format: 'cjs',
exports: 'auto'
},
{
file: 'dist/bundle.esm.js',
format: 'esm'
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'MyLibrary',
globals: {
'lodash': '_'
}
}
],
external: ['lodash'], // 外部依赖
plugins: [
resolve({
browser: true,
preferBuiltins: false
}),
commonjs(),
babel({
babelHelpers: 'bundled',
exclude: 'node_modules/**',
presets: ['@babel/preset-env']
}),
terser() // 压缩代码
]
});
// 多入口配置
const configs = [
{
input: 'src/main.js',
output: {
file: 'dist/main.js',
format: 'esm'
}
},
{
input: 'src/worker.js',
output: {
file: 'dist/worker.js',
format: 'iife'
}
}
];
export default configs.map(config => ({
...config,
plugins: [
resolve(),
commonjs(),
babel({ babelHelpers: 'bundled' })
]
}));
模块化最佳实践
// 1. 模块设计原则
// 单一职责原则
// user.js - 只处理用户相关逻辑
export class User {
constructor(id, name, email) {
this.id = id;
this.name = name;
this.email = email;
}
validate() {
return this.name && this.email && this.email.includes('@');
}
}
// user-service.js - 只处理用户服务逻辑
import { User } from './user.js';
export class UserService {
constructor(apiClient) {
this.apiClient = apiClient;
this.cache = new Map();
}
async getUser(id) {
if (this.cache.has(id)) {
return this.cache.get(id);
}
const userData = await this.apiClient.get(`/users/${id}`);
const user = new User(userData.id, userData.name, userData.email);
this.cache.set(id, user);
return user;
}
}
// 2. 接口隔离
// interfaces.js - 定义接口
export const ILogger = {
log: () => {},
error: () => {},
warn: () => {}
};
export const IStorage = {
get: () => {},
set: () => {},
remove: () => {},
clear: () => {}
};
// console-logger.js - 实现日志接口
export class ConsoleLogger {
log(message) {
console.log(`[LOG] ${message}`);
}
error(message) {
console.error(`[ERROR] ${message}`);
}
warn(message) {
console.warn(`[WARN] ${message}`);
}
}
// local-storage.js - 实现存储接口
export class LocalStorage {
get(key) {
try {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) : null;
} catch {
return null;
}
}
set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch {
return false;
}
}
remove(key) {
localStorage.removeItem(key);
}
clear() {
localStorage.clear();
}
}
// 3. 依赖倒置
// app.js - 高层模块不依赖低层模块
import { UserService } from './user-service.js';
import { ConsoleLogger } from './console-logger.js';
import { LocalStorage } from './local-storage.js';
import { ApiClient } from './api-client.js';
class Application {
constructor(logger, storage, apiClient) {
this.logger = logger;
this.storage = storage;
this.userService = new UserService(apiClient);
}
async start() {
this.logger.log('应用启动');
try {
const user = await this.userService.getUser(1);
this.storage.set('currentUser', user);
this.logger.log('用户加载成功');
} catch (error) {
this.logger.error('用户加载失败: ' + error.message);
}
}
}
// 依赖注入
const logger = new ConsoleLogger();
const storage = new LocalStorage();
const apiClient = new ApiClient('https://api.example.com');
const app = new Application(logger, storage, apiClient);
app.start();
// 4. 模块版本管理
// version.js - 版本信息
export const VERSION = '1.2.3';
export const BUILD_DATE = '2024-01-15';
export const COMMIT_HASH = 'abc123def456';
// feature-flags.js - 功能开关
export const FEATURES = {
NEW_UI: true,
BETA_FEATURE: false,
EXPERIMENTAL_API: process.env.NODE_ENV === 'development'
};
export function isFeatureEnabled(feature) {
return FEATURES[feature] || false;
}
// 5. 模块测试
// math.test.js - 模块测试
import { add, multiply, divide } from './math.js';
describe('Math Utils', () => {
test('add function', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
test('multiply function', () => {
expect(multiply(3, 4)).toBe(12);
expect(multiply(0, 5)).toBe(0);
});
test('divide function', () => {
expect(divide(10, 2)).toBe(5);
expect(() => divide(10, 0)).toThrow('除数不能为零');
});
});
// 6. 模块文档
/**
* 数学工具模块
* @module MathUtils
* @version 1.0.0
* @author Developer
* @since 2024-01-01
*/
/**
* 两数相加
* @param {number} a - 第一个数
* @param {number} b - 第二个数
* @returns {number} 两数之和
* @throws {Error} 当参数不是数字时抛出错误
* @example
* // 基本用法
* const result = add(2, 3); // 5
*
* // 错误处理
* try {
* add('2', 3);
* } catch (error) {
* console.error(error.message);
* }
*/
export function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('参数必须是数字');
}
return a + b;
}
本章总结
本章全面介绍了JavaScript的模块化编程:
- 模块化概述:理解了模块化的重要性和发展历程
- IIFE模块:学习了早期的模块化解决方案和设计模式
- CommonJS:掌握了Node.js的模块系统和使用方法
- ES6模块:了解了现代JavaScript的官方模块标准
- 打包工具:学习了Webpack和Rollup等构建工具的配置
- 最佳实践:掌握了模块设计原则、测试和文档编写
模块化是现代JavaScript开发的基础,它帮助我们构建可维护、可扩展的大型应用程序。通过合理的模块设计和工具使用,我们可以显著提高开发效率和代码质量。
下一章我们将学习JavaScript的面向对象编程,包括类、继承、多态等核心概念。