跳至内容
张叶安的小站
用户工具
登录
站点工具
搜索
工具
显示页面
过去修订
反向链接
最近更改
媒体管理器
网站地图
登录
>
最近更改
媒体管理器
网站地图
您的足迹:
javascript:第十章模块化
本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。
====== 第十章 模块化开发 ====== ===== 本章目标 ===== * 理解模块化编程的概念和重要性 * 掌握ES6模块(ESM)的语法和使用 * 了解CommonJS模块规范 * 学会使用模块打包工具 * 掌握模块的最佳实践 ===== 10.1 模块化的概念 ===== ==== 10.1.1 为什么需要模块化 ==== 随着JavaScript应用程序规模的增长,代码组织变得越来越重要。模块化编程提供了一种将代码分割成独立、可复用单元的方法。 **模块化带来的好处:** * **命名空间管理**:避免全局变量污染 * **代码复用**:模块可以在不同项目间共享 * **依赖管理**:清晰地声明模块间的依赖关系 * **可维护性**:代码结构清晰,易于理解和修改 * **作用域隔离**:每个模块有自己的作用域 ==== 10.1.2 JavaScript模块化的演进 ==== * **阶段一:全局函数** - 所有代码暴露在全局作用域 * **阶段二:立即执行函数(IIFE)** - 创建局部作用域 * **阶段三:CommonJS** - Node.js采用的模块规范 * **阶段四:AMD/CMD** - 浏览器端的异步模块定义 * **阶段五:ES6模块** - 语言级别的模块支持 ===== 10.2 ES6模块(ESM) ===== ==== 10.2.1 导出(export) ==== ES6使用export关键字导出模块中的变量、函数或类。 **命名导出:** <code javascript> // math.js export const PI = 3.14159; export function add(a, b) { return a + b; } export function subtract(a, b) { return a - b; } export class Calculator { multiply(a, b) { return a * b; } } </code> **默认导出:** <code javascript> // utils.js export default function greet(name) { return `你好,${name}!`; } // 一个模块只能有一个默认导出 // 但可以同时有命名导出 export const version = '1.0.0'; </code> **批量导出:** <code javascript> // api.js function getUsers() { /* ... */ } function getPosts() { /* ... */ } function createPost(data) { /* ... */ } export { getUsers, getPosts, createPost }; </code> ==== 10.2.2 导入(import) ==== **导入命名导出:** <code javascript> // main.js import { add, subtract, PI } from './math.js'; console.log(add(5, 3)); // 8 console.log(subtract(5, 3)); // 2 console.log(PI); // 3.14159 </code> **导入默认导出:** <code javascript> import greet from './utils.js'; import greet, { version } from './utils.js'; // 同时导入默认和命名 console.log(greet('张三')); // 你好,张三! </code> **重命名导入:** <code javascript> import { add as sum, subtract as minus } from './math.js'; console.log(sum(5, 3)); // 8 console.log(minus(5, 3)); // 2 </code> **导入所有内容:** <code javascript> import * as math from './math.js'; console.log(math.add(5, 3)); console.log(math.PI); const calc = new math.Calculator(); </code> ==== 10.2.3 模块路径 ==== <code javascript> // 相对路径 import { foo } from './module.js'; // 同级目录 import { bar } from '../utils/helper.js'; // 上级目录 import { baz } from './lib/sub.js'; // 子目录 // 绝对路径(从项目根目录) import { config } from '/src/config.js'; // URL路径 import { helper } from 'https://cdn.example.com/helper.js'; </code> ==== 10.2.4 动态导入 ==== 动态导入允许在运行时按需加载模块,返回一个Promise。 <code javascript> // 条件加载 if (userPreferDarkMode) { const darkTheme = await import('./themes/dark.js'); darkTheme.apply(); } // 懒加载 button.addEventListener('click', async () => { const { createChart } = await import('./chart.js'); createChart(data); }); // 根据条件选择模块 const lang = navigator.language; const messages = await import(`./locales/${lang}.js`); </code> ===== 10.3 CommonJS模块 ===== ==== 10.3.1 module.exports导出 ==== CommonJS是Node.js采用的模块规范,使用require()导入,module.exports导出。 <code javascript> // math.cjs const PI = 3.14159; function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } // 导出单个对象 module.exports = { PI, add, subtract }; // 或者分别导出 exports.PI = PI; exports.add = add; </code> ==== 10.3.2 require导入 ==== <code javascript> // main.cjs const math = require('./math.cjs'); console.log(math.add(5, 3)); // 8 console.log(math.subtract(5, 3)); // 2 console.log(math.PI); // 3.14159 // 解构导入 const { add, subtract } = require('./math.cjs'); console.log(add(5, 3)); // 8 </code> ==== 10.3.3 ES模块与CommonJS的互操作 ==== <code javascript> // 在ES模块中导入CommonJS模块 import { createRequire } from 'module'; const require = createRequire(import.meta.url); const cjsModule = require('./legacy.cjs'); // 在CommonJS中动态导入ES模块 async function loadESM() { const esmModule = await import('./modern.mjs'); return esmModule; } </code> ===== 10.4 模块设计模式 ===== ==== 10.4.1 单例模式模块 ==== <code javascript> // config.js class Config { #data = new Map(); set(key, value) { this.#data.set(key, value); } get(key) { return this.#data.get(key); } getAll() { return Object.fromEntries(this.#data); } } // 导出单例 export default new Config(); </code> ==== 10.4.2 工厂模式模块 ==== <code javascript> // validator.js const validators = { email(value) { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(value); }, phone(value) { const regex = /^1[3-9]\d{9}$/; return regex.test(value); }, required(value) { return value !== undefined && value !== null && value !== ''; }, minLength(value, length) { return String(value).length >= length; }, maxLength(value, length) { return String(value).length <= length; }, range(value, min, max) { return value >= min && value <= max; } }; export function createValidator(rules) { return function validate(data) { const errors = {}; for (const [field, fieldRules] of Object.entries(rules)) { for (const rule of fieldRules) { const [ruleName, ...params] = rule.split(':'); const validator = validators[ruleName]; if (!validator) { throw new Error(`未知的验证规则: ${ruleName}`); } const isValid = validator(data[field], ...params); if (!isValid) { errors[field] = errors[field] || []; errors[field].push(`${field}验证失败: ${ruleName}`); } } } return { isValid: Object.keys(errors).length === 0, errors }; }; } </code> ==== 10.4.3 观察者模式模块 ==== <code javascript> // eventBus.js class EventBus { #events = new Map(); on(event, callback) { if (!this.#events.has(event)) { this.#events.set(event, []); } this.#events.get(event).push(callback); // 返回取消订阅函数 return () => this.off(event, callback); } off(event, callback) { if (!this.#events.has(event)) return; const callbacks = this.#events.get(event); const index = callbacks.indexOf(callback); if (index > -1) { callbacks.splice(index, 1); } } emit(event, data) { if (!this.#events.has(event)) return; this.#events.get(event).forEach(callback => { try { callback(data); } catch (error) { console.error('事件处理错误:', error); } }); } once(event, callback) { const onceCallback = (data) => { this.off(event, onceCallback); callback(data); }; this.on(event, onceCallback); } } export const eventBus = new EventBus(); export default EventBus; </code> ===== 10.5 模块组织与架构 ===== ==== 10.5.1 项目结构示例 ==== <code> project/ ├── src/ │ ├── main.js # 入口文件 │ ├── config/ │ │ ├── index.js # 配置导出 │ │ └── database.js # 数据库配置 │ ├── utils/ │ │ ├── index.js # 工具函数导出 │ │ ├── date.js # 日期工具 │ │ ├── format.js # 格式化工具 │ │ └── validator.js # 验证工具 │ ├── components/ │ │ ├── Button.js │ │ ├── Input.js │ │ └── index.js # 组件统一导出 │ ├── services/ │ │ ├── api.js # API服务 │ │ ├── auth.js # 认证服务 │ │ └── user.js # 用户服务 │ └── styles/ │ ├── variables.css │ └── main.css ├── package.json └── index.html </code> ==== 10.5.2 索引文件模式 ==== <code javascript> // utils/index.js export { formatDate, parseDate } from './date.js'; export { formatCurrency, formatNumber } from './format.js'; export { createValidator } from './validator.js'; // main.js import { formatDate, formatCurrency, createValidator } from './utils/index.js'; // 或者简写 import { formatDate } from './utils/index.js'; </code> ==== 10.5.3 配置驱动的模块加载 ==== <code javascript> // plugins/index.js const pluginModules = import.meta.glob('./*.js'); export async function loadPlugins() { const plugins = []; for (const path in pluginModules) { const module = await pluginModules[path](); plugins.push(module.default); } return plugins; } // main.js import { loadPlugins } from './plugins/index.js'; const plugins = await loadPlugins(); plugins.forEach(plugin => plugin.install()); </code> ===== 10.6 模块打包与构建 ===== ==== 10.6.1 常用构建工具 ==== * **Webpack** - 功能全面的模块打包器 * **Vite** - 基于ES模块的快速构建工具 * **Rollup** - 专注于ES模块的打包器 * **Parcel** - 零配置的打包工具 ==== 10.6.2 Tree Shaking ==== Tree Shaking是一种消除死代码的优化技术,只打包实际使用的代码。 <code javascript> // utils.js export function used() { return '这个函数会被打包'; } export function unused() { return '这个函数会被移除'; } // main.js import { used } from './utils.js'; console.log(used()); // unused函数不会被打包 </code> ==== 10.6.3 代码分割 ==== <code javascript> // 路由级别的代码分割 const routes = [ { path: '/dashboard', component: () => import('./pages/Dashboard.js') }, { path: '/profile', component: () => import('./pages/Profile.js') }, { path: '/settings', component: () => import('./pages/Settings.js') } ]; </code> ===== 10.7 模块最佳实践 ===== ==== 10.7.1 导出规范 ==== <code javascript> // ✅ 推荐:明确的命名导出 export function helper() { } export const CONFIG = { }; // ✅ 推荐:默认导出用于主要功能 export default class MainComponent { } // ❌ 避免:混合多种导出方式造成困惑 export function helper() { } export default function main() { } </code> ==== 10.7.2 避免循环依赖 ==== <code javascript> // ❌ 避免:a.js 导入 b.js,b.js 又导入 a.js // a.js import { b } from './b.js'; export const a = 'A'; // b.js import { a } from './a.js'; // 循环依赖! export const b = 'B'; // ✅ 解决:提取公共代码到单独的模块 // types.js export const a = 'A'; export const b = 'B'; </code> ==== 10.7.3 副作用管理 ==== <code javascript> // polyfill.js // 有副作用的模块(修改全局对象) if (!Array.prototype.flat) { Array.prototype.flat = function(depth = 1) { // 实现... }; } // 导入副作用模块 import './polyfill.js'; </code> ===== 本章习题 ===== ==== 基础练习 ==== **练习1:基本模块** 创建一个math.js模块,导出基本的数学运算函数(加、减、乘、除),在main.js中导入并使用。 **练习2:默认与命名导出** 创建一个dateUtils.js模块,默认导出formatDate函数,命名导出parseDate和isValidDate函数。 **练习3:模块重构** 将以下代码重构为模块形式: <code javascript> const users = []; function addUser(user) { users.push(user); } function getUser(id) { return users.find(u => u.id === id); } function deleteUser(id) { const index = users.findIndex(u => u.id === id); if (index > -1) { users.splice(index, 1); } } </code> ==== 进阶练习 ==== **练习4:动态模块加载** 实现一个插件系统,根据配置文件动态加载不同的插件模块。 **练习5:模块测试** 为练习3的用户管理模块编写单元测试。 **练习6:Monorepo结构** 设计一个包含多个包的Monorepo项目结构,包括core、ui、utils三个子包。 ==== 思考题 ==== 1. ES模块和CommonJS模块的主要区别是什么?各自适用于什么场景? 2. 动态导入有什么优势?在什么情况下应该使用? 3. 如何避免模块间的循环依赖? 4. Tree Shaking是如何工作的?如何编写对Tree Shaking友好的代码? ===== 参考答案 ===== **练习3答案:** <code javascript> // userService.js const users = []; export function addUser(user) { users.push(user); } export function getUser(id) { return users.find(u => u.id === id); } export function deleteUser(id) { const index = users.findIndex(u => u.id === id); if (index > -1) { users.splice(index, 1); } } export function getAllUsers() { return [...users]; } // main.js import { addUser, getUser, deleteUser } from './userService.js'; addUser({ id: 1, name: '张三' }); console.log(getUser(1)); deleteUser(1); </code> **练习4答案:** <code javascript> // pluginManager.js export class PluginManager { #plugins = new Map(); async load(pluginPath) { try { const module = await import(pluginPath); const plugin = module.default || module; if (typeof plugin.install !== 'function') { throw new Error('插件必须提供install方法'); } this.#plugins.set(plugin.name, plugin); return plugin; } catch (error) { console.error(`加载插件失败 ${pluginPath}:`, error); throw error; } } async installAll(config) { for (const pluginConfig of config.plugins) { const plugin = await this.load(pluginConfig.path); plugin.install(pluginConfig.options); } } get(name) { return this.#plugins.get(name); } } // config.json { "plugins": [ { "path": "./plugins/logger.js", "options": { "level": "debug" } }, { "path": "./plugins/analytics.js", "options": { "trackingId": "UA-xxx" } } ] } </code> ===== 小结 ===== 本章我们学习了JavaScript模块化开发的各个方面: * **模块化的意义**:代码组织、复用、维护 * **ES6模块**:import/export语法、默认导出、动态导入 * **CommonJS**:require/module.exports、Node.js环境 * **设计模式**:单例、工厂、观察者模式在模块中的应用 * **项目架构**:目录结构、索引文件、配置驱动 * **构建优化**:Tree Shaking、代码分割 * **最佳实践**:导出规范、避免循环依赖、副作用管理 模块化是现代JavaScript开发的基石,掌握模块化的思想和技巧对于构建可维护的大型应用至关重要。
javascript/第十章模块化.txt
· 最后更改:
2026/02/03 22:33
由
127.0.0.1
页面工具
显示页面
过去修订
反向链接
回到顶部