模块系统
概述
JavaScript 模块系统是现代前端工程化的基石。从早期的全局变量满天飞,到 CommonJS 的服务器端统治,再到 ES Modules(ESM)成为浏览器和 Node.js 的统一标准,模块系统经历了漫长的演进。本文将全面解析 ES Modules 的完整语法,对比 CommonJS 与 ESM 的差异,深入剖析模块解析算法,并探讨循环依赖的处理策略。
ES Modules 完整语法
import/export 基础
ES Modules 使用 import 和 export 关键字实现模块的导入导出,支持多种导出导入方式:
// === named-export.js ===
// 命名导出:可以导出多个
export const PI = 3.14159;
export const E = 2.71828;
export function add(a, b) {
return a + b;
}
export class Calculator {
constructor(initialValue = 0) {
this.value = initialValue;
}
add(n) {
this.value += n;
return this;
}
}
// === default-export.js ===
// 默认导出:每个模块只能有一个
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
export default config;
// === combined-export.js ===
// 混合导出:同时有默认导出和命名导出
export const VERSION = '1.0.0';
export const API_KEY = 'abc123';
const appConfig = { /* ... */ };
export default appConfig;// === consumer.js ===
// 命名导入
import { PI, E, add, Calculator } from './named-export.js';
// 重命名导入
import { PI as PI_VALUE, add as sum } from './named-export.js';
// 导入所有命名导出
import * as constants from './named-export.js';
console.log(constants.PI); // 3.14159
// 默认导入
import config from './default-export.js';
console.log(config.apiUrl);
// 混合导入(默认和命名分开)
import defaultExport, { VERSION, API_KEY } from './combined-export.js';
// 动态导入
const module = await import('./dynamic-module.js');
module.someFunction();动态导入
动态 import() 返回一个 Promise,支持代码分割和按需加载:
class ModuleLoader {
static cache = new Map();
static async load(modulePath, options = {}) {
const { cache = true, reload = false } = options;
// 使用缓存
if (cache && this.cache.has(modulePath) && !reload) {
return this.cache.get(modulePath);
}
try {
const module = await import(/* webpackChunkName: "[request]" */ modulePath);
if (cache) {
this.cache.set(modulePath, module);
}
return module;
} catch (error) {
console.error(`Failed to load module: ${modulePath}`, error);
throw error;
}
}
static async loadMultiple(modulePaths) {
const modules = await Promise.all(
modulePaths.map(path => this.load(path))
);
return modules;
}
static async loadWithFallback(primary, fallback) {
try {
return await this.load(primary);
} catch {
return await this.load(fallback);
}
}
static clearCache() {
this.cache.clear();
}
}
// 路由懒加载示例
class Router {
constructor() {
this.routes = new Map();
}
register(path, modulePath) {
this.routes.set(path, modulePath);
}
async navigate(path) {
const modulePath = this.routes.get(path);
if (!modulePath) {
throw new Error(`Route not found: ${path}`);
}
// 动态加载路由模块
const { component } = await ModuleLoader.load(modulePath);
return component;
}
}
// 使用示例
const router = new Router();
router.register('/dashboard', './routes/Dashboard.js');
router.register('/settings', './routes/Settings.js');
router.register('/profile', './routes/Profile.js');
// 条件加载
async function loadFeature() {
if (featureIsEnabled('newFeature')) {
const { newFeature } = await import('./features/NewFeature.js');
return newFeature;
}
}import() 高级用法
// 预加载模块
class ModulePreloader {
static preload(routes) {
return routes.map(route => {
// 静默预加载
import(route.modulePath).catch(() => {});
return route;
});
}
static async preloadOnIdle(routes) {
return new Promise((resolve) => {
if ('requestIdleCallback' in window) {
requestIdleCallback(async () => {
await Promise.all(
routes.map(route => import(route.modulePath))
);
resolve();
});
} else {
setTimeout(async () => {
await Promise.all(
routes.map(route => import(route.modulePath))
);
resolve();
}, 1000);
}
});
}
}
// 批量导入管理
class ModuleRegistry {
constructor() {
this.modules = new Map();
this.loading = new Map();
}
async register(name, loader) {
if (this.modules.has(name)) {
return this.modules.get(name);
}
if (this.loading.has(name)) {
return this.loading.get(name);
}
const promise = (async () => {
const module = await loader();
this.modules.set(name, module);
this.loading.delete(name);
return module;
})();
this.loading.set(name, promise);
return promise;
}
get(name) {
return this.modules.get(name);
}
has(name) {
return this.modules.has(name);
}
}
// 使用示例
const registry = new ModuleRegistry();
registry.register('auth', () => import('./services/Auth.js'));
registry.register('api', () => import('./services/API.js'));
registry.register('storage', () => import('./services/Storage.js'));
// 后续获取
const { auth } = await registry.get('auth');re-export 语法
// re-export 所有
export * from './constants.js';
// re-export 并重命名
export { PI, E } from './constants.js';
export { PI as π, E as e } from './constants.js';
// re-export 默认导出
export { default } from './config.js';
export { default as config } from './config.js';
// 统一导出接口
export { default as Component, Component as BaseComponent } from './Component.js';
export { default as Hook, useState, useEffect } from './hooks.js';CommonJS vs ESM 对比
核心差异
| 特性 | CommonJS | ES Modules |
|---|---|---|
| 语法 | require() / module.exports | import / export |
| 加载时机 | 同步(运行时加载) | 异步(编译时解析) |
| 值引用 | 值拷贝 | 实时绑定(Live Bindings) |
| 顶层层级 | 无限制 | 顶层 await / import |
| 循环引用 | 容易出问题 | 有更好的处理机制 |
| 动态性 | 动态 require | 仅静态 import |
| Node.js 支持 | 原生 | 实验性/需设置 type: module |
| 浏览器支持 | 不支持 | 原生支持 |
代码对比
// === CommonJS 版本 ===
// 导出
const config = { apiUrl: 'https://api.example.com' };
const helper = () => { /* ... */ };
module.exports = config;
module.exports.helper = helper;
// 或者
module.exports = { config, helper };
// 导入
const { config, helper } = require('./module');
const module = require('./module');
// === ES Modules 版本 ===
// 导出
export const config = { apiUrl: 'https://api.example.com' };
export const helper = () => { /* ... */ };
// 或者
export default { config, helper };
// 导入
import { config, helper } from './module.mjs';
import module from './module.mjs';值引用差异
// === counter.js ===
// CommonJS - 值拷贝
let count = 0;
const increment = () => ++count;
const getCount = () => count;
module.exports = { count, increment, getCount };
// === main.js (CommonJS) ===
const { count, increment, getCount } = require('./counter');
console.log(count); // 0
console.log(getCount()); // 0
increment();
increment();
console.log(count); // 0 (仍然是拷贝的值)
console.log(getCount()); // 2
// === ES Modules - 实时绑定 ===
let count = 0;
export const increment = () => ++count;
export const getCount = () => count;
// === main.js (ESM) ===
import { count, increment, getCount } from './counter.mjs';
console.log(count); // 0
console.log(getCount()); // 0
increment();
increment();
console.log(count); // 2 (实时绑定)
console.log(getCount()); // 2混合使用
// === wrapper.cjs ===
// 在 CommonJS 中使用 ESM
const { esmFunc } = await import('./esm-module.mjs');
console.log(esmFunc());
// === esm-wrapper.mjs ===
// 在 ESM 中使用 CommonJS
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { cjsFunc } = require('./cjs-module.cjs');
console.log(cjsFunc());模块解析算法
解析流程
ES Modules 使用统一的模块解析算法,主要步骤:
-
确定模块说明符类型
- 相对路径(
./,../) - 绝对路径(
/) - URL(
https://...) - bare specifier(无路径,如
lodash)
- 相对路径(
-
应用模块说明符扩展
- 自动添加
.js,.mjs,.cjs扩展名 - 检查目录下的
package.json的exports字段 - 查找
index.js,index.mjs
- 自动添加
-
符号链接解析
- 解析
node_modules目录 - 向上遍历目录树
- 处理符号链接
- 解析
路径解析示例
// src/utils/helpers.js
// 模块说明符类型解析
// 1. 相对路径
import { a } from './module.js'; // → ./module.js
import { b } from './module'; // → ./module.js/.mjs/.cjs
// 2. 绝对路径
import { c } from '/absolute/path.js'; // → /absolute/path.js
// 3. 裸说明符(查找 node_modules)
import _ from 'lodash'; // → node_modules/lodash
import { d } from '@org/package'; // → node_modules/@org/package
// 4. URL
import { e } from 'https://example.com/module.js';package.json exports 字段
{
"name": "my-package",
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
},
"./utils": {
"import": "./dist/esm/utils.js",
"require": "./dist/cjs/utils.js"
},
"./nested/path": "./dist/path.js",
"./features/*": "./dist/features/*.js",
"./internal": {
"import": "./src/internal/index.js",
"node": "./src/internal/node.js",
"default": "./dist/internal.js"
}
}
}自定义解析器
// 自定义模块解析逻辑
class ModuleResolver {
constructor(options = {}) {
this.baseURL = options.baseURL || process.cwd();
this.paths = options.paths || [];
this.extensions = options.extensions || ['.js', '.mjs', '.cjs'];
this alias = options.alias || {};
}
resolve(specifier, parentURL) {
// 1. 处理别名
const aliased = this.resolveAlias(specifier);
// 2. 解析为绝对路径
const absolutePath = this.toAbsolute(aliased, parentURL);
// 3. 应用扩展名
const finalPath = this.applyExtension(absolutePath);
// 4. 检查文件是否存在
if (this.exists(finalPath)) {
return { url: `file://${finalPath}`, shortCircuit: true };
}
// 5. 检查 package.json exports
const packageExport = this.resolvePackageExport(absolutePath);
if (packageExport) {
return { url: `file://${packageExport}`, shortCircuit: true };
}
throw new Error(`Cannot find module: ${specifier}`);
}
resolveAlias(specifier) {
for (const [pattern, replacement] of Object.entries(this.alias)) {
if (specifier.startsWith(pattern)) {
return specifier.replace(pattern, replacement);
}
}
return specifier;
}
toAbsolute(specifier, parentURL) {
if (specifier.startsWith('./') || specifier.startsWith('../')) {
return new URL(specifier, parentURL).pathname;
}
if (specifier.startsWith('/')) {
return specifier;
}
// 裸说明符:查找 node_modules
return this.resolveInNodeModules(specifier, parentURL);
}
applyExtension(path) {
if (this.exists(path)) return path;
for (const ext of this.extensions) {
const withExt = path + ext;
if (this.exists(withExt)) {
return withExt;
}
}
// 检查 index 文件
for (const ext of this.extensions) {
const indexPath = path + '/index' + ext;
if (this.exists(indexPath)) {
return indexPath;
}
}
return path;
}
resolveInNodeModules(specifier, parentURL) {
let dir = parentURL ? new URL('.', parentURL).pathname : this.baseURL;
while (dir !== '/') {
const nodeModules = dir + 'node_modules/' + specifier;
if (this.exists(nodeModules)) {
return this.applyExtension(nodeModules);
}
dir = dir.replace(/\/[^/]+\/$/, '');
}
throw new Error(`Cannot find module: ${specifier}`);
}
exists(path) {
try {
require('fs').accessSync(path);
return true;
} catch {
return false;
}
}
}
// 使用示例
const resolver = new ModuleResolver({
baseURL: '/project/src',
alias: {
'@utils': '/project/src/utils',
'@components': '/project/src/components',
'@styles': '/project/src/styles'
},
extensions: ['.js', '.mjs', '.ts', '.tsx']
});
const resolved = resolver.resolve('@utils/format', '/project/src/app.js');
console.log(resolved); // { url: 'file:///project/src/utils/format.js', shortCircuit: true }循环依赖处理
循环依赖的本质
循环依赖发生在两个或多个模块相互引用时:
// a.js
import { b } from './b.js';
export const a = 'a';
export function getA() { return b; }
// b.js
import { a } from './a.js';
export const b = 'b';
export function getB() { return a; }ESM 循环依赖处理机制
ES Modules 使用**实时绑定(Live Bindings)**来处理循环依赖:
// === module-a.mjs ===
import { b, setB } from './module-b.mjs';
export const a = 'module A';
export function getBValue() {
return b; // 此时 b 可能尚未初始化,但会返回其最终值
}
console.log('A: b =', b); // undefined (b 尚未初始化)
console.log('A: getBValue() =', getBValue()); // 可以工作
setB('B has been set');// === module-b.mjs ===
import { a } from './module-a.mjs';
export let b = 'initial B'; // 使用 let 以支持重新赋值
export function setB(value) {
b = value;
}
export function getAValue() {
return a; // 'module A' (a 已经初始化)
}
console.log('B: a =', a); // 'module A'
console.log('B: getAValue() =', getAValue()); // 'module A'循环依赖解决方案
方案一:延迟访问
// === service-a.mjs ===
import { callB } from './service-b.mjs';
export const serviceA = {
name: 'Service A',
initialize() {
console.log('Service A initializing');
// 延迟调用,等待其他模块完全初始化
setTimeout(() => callB(), 0);
},
method() {
return 'A method';
}
};
console.log('Service A module loaded');// === service-b.mjs ===
import { serviceA } from './service-a.mjs';
export function callB() {
console.log('Calling B, A available:', !!serviceA);
console.log('A name:', serviceA?.name);
}
console.log('Service B module loaded');方案二:提取共享模块
// === shared.mjs ===
// 创建独立的共享模块,打破循环
export const state = {
initialized: false
};
export const registry = {
services: new Map(),
register(name, service) {
this.services.set(name, service);
},
get(name) {
return this.services.get(name);
}
};// === service-a.mjs ===
import { registry } from './shared.mjs';
export const serviceA = {
name: 'Service A',
initialize() {
registry.register('serviceA', this);
registry.get('serviceB')?.method();
}
};// === service-b.mjs ===
import { registry } from './shared.mjs';
export const serviceB = {
name: 'Service B',
initialize() {
registry.register('serviceB', this);
registry.get('serviceA')?.method();
}
};方案三:工厂函数模式
// === factory-a.mjs ===
let instance = null;
export function createServiceA(deps) {
if (instance) return instance;
instance = {
name: 'Service A',
method() {
// 延迟访问依赖
return deps.getB?.().method() || 'B not available';
}
};
return instance;
}
export function getServiceA() {
return instance;
}// === dependency-injector.mjs ===
import { createServiceA } from './factory-a.mjs';
import { createServiceB } from './factory-b.mjs';
class Container {
constructor() {
this.services = new Map();
}
register(name, factory) {
this.services.set(name, factory);
}
get(name) {
const factory = this.services.get(name);
if (!factory) return null;
// 单例缓存
if (!this.cache) this.cache = new Map();
if (this.cache.has(name)) return this.cache.get(name);
const instance = factory(this); // 传入容器自身
this.cache.set(name, instance);
return instance;
}
}
// 创建并配置容器
const container = new Container();
container.register('serviceA', (c) => createServiceA({
getB: () => c.get('serviceB')
}));
container.register('serviceB', (c) => createServiceB({
getA: () => c.get('serviceA')
}));
// 使用
const serviceA = container.get('serviceA');
const serviceB = container.get('serviceB');方案四:事件驱动解耦
// === event-bus.mjs ===
class EventBus {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
}
off(event, callback) {
const callbacks = this.listeners.get(event);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) callbacks.splice(index, 1);
}
}
emit(event, data) {
const callbacks = this.listeners.get(event);
if (callbacks) {
callbacks.forEach(cb => cb(data));
}
}
}
export const eventBus = new EventBus();// === module-a.mjs ===
import { eventBus } from './event-bus.mjs';
import { getB } from './module-b.mjs';
export function initA() {
eventBus.on('b:ready', () => {
console.log('B is ready, proceeding with A');
const b = getB();
b?.doSomething();
});
}
export function getA() {
return { name: 'A' };
}// === module-b.mjs ===
import { eventBus } from './event-bus.mjs';
let b = null;
export function initB() {
b = { name: 'B', doSomething: () => {} };
eventBus.emit('b:ready', b);
}
export function getB() {
return b;
}检测循环依赖工具
// === circular-detector.mjs ===
export function detectCircularDependencies(entryModule) {
const visited = new Set();
const recursionStack = new Set();
const circularPaths = [];
function dfs(modulePath, path) {
if (recursionStack.has(modulePath)) {
const cycleStart = path.indexOf(modulePath);
circularPaths.push([...path.slice(cycleStart), modulePath].join(' → '));
return;
}
if (visited.has(modulePath)) return;
visited.add(modulePath);
recursionStack.add(modulePath);
path.push(modulePath);
try {
const module = import(modulePath);
// 分析导入的模块...
// 这里需要静态分析工具
} catch (error) {
// 模块加载失败
}
path.pop();
recursionStack.delete(modulePath);
}
dfs(entryModule, []);
return circularPaths;
}核心概念与设计哲学
模块化的演进历程
JavaScript 模块系统的发展史是一部”从混沌到秩序”的演进史。理解这段历史,有助于理解为什么 ES Modules 要这样设计。
JavaScript 模块化演进
├── 1995-2009: 无模块时代
│ ├── 全局变量满天飞
│ ├── 命名空间模式(勉强救场)
│ └── IIFE 闭包封装
│
├── 2009-2015: CommonJS 时代
│ ├── Node.js 采用 CommonJS
│ ├── require()/module.exports
│ └── 同步加载,适合服务器端
│
├── 2010-2015: AMD/CMD 时代
│ ├── RequireJS (AMD)
│ ├── SeaJS (CMD)
│ └── 异步加载,适合浏览器
│
├── 2015至今: ES Modules 时代
│ ├── import/export 原生支持
│ ├── 静态分析友好
│ └── 浏览器和 Node.js 统一标准
模块化的设计原则
1. 单一职责原则(SRP) 每个模块应该只负责一件事。这使得模块易于理解、测试和复用。
// 好的模块设计:职责单一
// validators/email.js
export function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
// validators/password.js
export function validatePassword(password) {
return password.length >= 8 && /\d/.test(password);
}
// validators/username.js
export function validateUsername(username) {
return /^[a-zA-Z0-9_]{3,20}$/.test(username);
}
// 差的模块设计:职责混杂
// validator-all-in-one.js
export function validate(value, type) {
// 混杂的验证逻辑
}2. 开放封闭原则(OCP) 模块应该对扩展开放,对修改封闭。使用装饰器模式可以实现这一原则。
// 基类模块
export class Validator {
validate(value) {
throw new Error('Must be implemented');
}
}
// 扩展:组合验证器
export class CompositeValidator extends Validator {
constructor(validators) {
super();
this.validators = validators;
}
validate(value) {
for (const validator of this.validators) {
if (!validator.validate(value)) {
return false;
}
}
return true;
}
}
// 使用
const validator = new CompositeValidator([
new EmailValidator(),
new LengthValidator(8),
]);3. 依赖倒置原则(DIP) 高层模块不应该依赖低层模块,两者都应该依赖抽象。
// 抽象层
export class Storage {
async get(key) { throw new Error('Not implemented'); }
async set(key, value) { throw new Error('Not implemented'); }
async remove(key) { throw new Error('Not implemented'); }
}
// 低层实现
export class LocalStorageAdapter extends Storage {
async get(key) { return localStorage.getItem(key); }
async set(key, value) { localStorage.setItem(key, value); }
async remove(key) { localStorage.removeItem(key); }
}
export class SessionStorageAdapter extends Storage {
async get(key) { return sessionStorage.getItem(key); }
async set(key, value) { sessionStorage.setItem(key, value); }
async remove(key) { sessionStorage.removeItem(key); }
}
// 高层模块依赖抽象
export class UserService {
constructor(storage) {
this.storage = storage; // 注入具体实现
}
async saveUser(user) {
await this.storage.set('user', JSON.stringify(user));
}
}模块系统的核心价值
模块系统的核心价值
├── 命名空间隔离
│ ├── 避免全局变量污染
│ └── 防止命名冲突
│
├── 依赖管理
│ ├── 显式声明依赖
│ ├── 版本控制
│ └── 避免循环依赖
│
├── 代码组织
│ ├── 按功能分块
│ ├── 便于维护
│ └── 提高可读性
│
├── 复用性
│ ├── 跨项目共享
│ ├── npm 生态
│ └── 组件化开发
│
└── 性能优化
├── 按需加载
├── 代码分割
└── 缓存优化
详细原理解析
ES Modules 执行机制
ES Modules 的执行分为三个阶段:构建(Construction)、实例化(Instantiation)、求值(Evaluation)。这个过程称为”宿主机定义环境(Host Defined Environment)“的模块记录(Module Records)创建过程。
// 阶段 1:构建 - 解析所有模块,生成 Module Records
// 阶段 2:实例化 - 建立模块之间的链接,创建模块环境记录
// 阶段 3:求值 - 执行模块顶层代码
// graph TD
// A[index.html] --> B[main.js]
// B --> C[module-a.js]
// B --> D[module-b.js]
// C --> D
// 具体执行流程:
// 1. 遇到 import,先解析该模块
// 2. 所有模块解析完成后,开始实例化
// 3. 实例化完成后,按深度优先后序遍历执行Live Bindings 机制
ES Modules 的导入导出使用”实时绑定”,而非值拷贝:
// counter.mjs
export let count = 0;
export function increment() {
count++;
console.log('Incremented to:', count);
}
export function reset() {
count = 0;
console.log('Reset to:', count);
}
// main.mjs
import { count, increment, reset } from './counter.mjs';
console.log('Initial count:', count); // 0
increment(); // count 变为 1
console.log('After increment:', count); // 1
// 导出的模块可以修改 count
import { increment } from './counter.mjs';
increment(); // 再次改变 count
console.log('Final count:', count); // 2
// 注意:导入的绑定不能被重新赋值
import { count } from './counter.mjs';
count = 10; // SyntaxError: Assignment to constant variable.
// 但可以通过导出的函数修改
reset(); // count 被重置为 0模块循环引用处理
// a.mjs
import { b } from './b.mjs';
export const a = 'module A';
export function getA() { return b; }
console.log('A: b =', b); // undefined (b 尚未初始化)
// b.mjs
import { a } from './a.mjs';
export let b = 'module B'; // 使用 let 以便后续修改
export function getB() { return a; }
console.log('B: a =', a); // undefined (a 尚未初始化)
// 解决方案:使用函数延迟访问
// a.mjs
import { b } from './b.mjs';
export const a = 'module A';
export function getA() { return b; }
console.log('A: calling getB() =', getB()); // 正常工作
// b.mjs
import { getA } from './a.mjs';
export let b = 'module B';
export function getB() { return getA(); }
console.log('B: calling getA() =', getA()); // 正常工作模块解析算法详解
ES Modules 使用统一的解析算法,考虑多种场景:
// 解析流程图
// 1. 解析模块说明符
// 2. 确定模块位置
// 3. 加载模块代码
// 4. 链接模块依赖
// 模块说明符类型
// 1. 相对说明符: ./module.js, ../utils/helper.js
// 2. 绝对说明符: /path/to/module.js
// 3. 裸说明符: lodash, @org/package
// 4. URL 说明符: https://example.com/module.js
// 解析规则示例
// 输入: import x from './utils' (from /project/src/app.js)
// 1. 解析为 URL: /project/src/utils
// 2. 尝试添加扩展: .js, .mjs, .cjs
// 3. 尝试作为目录: /project/src/utils/index.js
// 4. 检查 package.json exports
// 5. 抛出错误或返回解析结果Node.js 模块解析细节
// Node.js 解析顺序
// 1. 内置模块 (fs, path, http 等)
// 2. 文件模块 (相对/绝对路径)
// 3. node_modules 包
// package.json 优先级
// 1. package.json 的 main 字段
// 2. package.json 的 exports 字段 (ESM 优先)
// 3. index.js, index.json, index.node
// exports 字段详细配置
{
"name": "my-package",
"exports": {
// 条件导出
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts",
"default": "./dist/esm/index.js"
},
// 子路径导出
"./utils": "./dist/utils.js",
"./features/*": "./dist/features/*.js",
// 条件子路径
"./sub/path": {
"import": {
"types": "./dist/esm/sub/path.d.ts",
"default": "./dist/esm/sub/path.js"
},
"require": {
"types": "./dist/cjs/sub/path.d.ts",
"default": "./dist/cjs/sub/path.js"
}
}
}
}动态导入实现原理
import() 表达式返回 Promise,实现按需加载:
// 基本实现
const module = await import('./module.js');
module.default; // 默认导出
module.namedExport; // 命名导出
// 底层原理
// 1. import() 创建新的模块记录
// 2. 发起网络请求加载模块代码
// 3. 解析为 Module Record
// 4. 与已有模块图合并
// 5. 返回 Promise,在模块求值完成后 resolve
// 应用场景
// 1. 路由懒加载
const routes = {
home: () => import('./pages/Home.js'),
about: () => import('./pages/About.js'),
};
// 2. 条件加载
if (isFeatureEnabled('newUI')) {
const { NewComponent } = await import('./new-ui.js');
}
// 3. 错误恢复
try {
const module = await import('./optional.js');
} catch {
// 使用降级方案
}
// 4. 预加载
const preloadPromise = import('./heavy-module.js');
// 在需要时使用
const { heavyFunction } = await preloadPromise;模块与浏览器
浏览器中的加载流程
// HTML 中的模块加载
<script type="module" src="./app.js"></script>
// 特点:
// 1. 自动 defer
// 2. CORS 跨域要求
// 3. 始终以严格模式运行
// 内联模块
<script type="module">
import { something } from './module.js';
// 代码
</script>
// nomodule 回退
<script nomodule src="./legacy-bundle.js"></script>
// 预加载
<link rel="modulepreload" href="./app.js">
<link rel="modulepreload" href="./dependency.js">浏览器兼容性与注意事项
// CORS 要求
// 所有模块必须满足同源策略或 CORS 头部
// Content-Type: text/javascript 或 application/javascript
// MIME 类型要求
// 正确: application/javascript, text/javascript
// 错误: text/plain, application/octet-stream
// 缓存行为
// 1. 模块 URL 作为缓存键
// 2. 修改 URL 可绕过缓存
// 3. 使用 import maps 优化
// import maps (ES2023+)
{
"imports": {
"lodash": "https://cdn.example.com/lodash@4.17.21/index.js",
"react": "https://cdn.example.com/react@18.2.0/index.js"
}
}常用 API 详解
import API
// 基本导入
import defaultExport from 'module-name';
import * as name from 'module-name';
import { export1 } from 'module-name';
import { export1 as alias1 } from 'module-name';
import { export1, export2 } from 'module-name';
import { export1, export2 as alias2 } from 'module-name';
import defaultExport, { export1 } from 'module-name';
import defaultExport, * as name from 'module-name';
// 副作用导入(仅执行模块顶层代码)
import 'module-name';
// 动态导入
import('module-name').then(module => {
// 使用 module.default 或 module.namedExport
});
// await 用法(模块顶层)
const module = await import('module-name');
// 带条件的导入
const module = condition ? await import('./a.js') : await import('./b.js');export API
// 命名导出(在声明时)
export const name = 'value';
export let name = 'value';
export function func() { /* ... */ }
export class MyClass { /* ... */ }
// 命名导出(列表形式)
const name = 'value';
const other = 'other';
export { name, other };
export { name as alias };
export { name as alias1, other as alias2 };
// 默认导出
export default expression;
export default function func() { /* ... */ }
export default class MyClass { /* ... */ }
// 重新导出
export { export1 } from 'module-name';
export { export1 as alias } from 'module-name';
export { default } from 'module-name';
export { default as name } from 'module-name';
export * from 'module-name';import.meta API
// import.meta.url - 当前模块的 URL
// 浏览器: window.location.href 相关
// Node.js: file:// URL
// 浏览器环境
const currentModuleURL = import.meta.url;
// Node.js 环境
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// import.meta.resolve - 解析模块路径
const resolvedURL = import.meta.resolve('./utils.js');
const resolved = import.meta.resolve('lodash');
// import.meta.env - 环境变量
// Vite/Webpack 等工具提供
import.meta.env.MODE; // 'development' | 'production'
import.meta.env.DEV; // boolean
import.meta.env.PROD; // boolean
import.meta.env.BASE_URL; // 部署基础路径
import.meta.env.SSR; // 是否在 SSR 环境
// 自定义环境变量(需要前缀)
// VITE_API_URL, PUBLIC_API_KEY 等实战代码示例
构建自己的模块加载器
// mini-module-loader.mjs
class ModuleLoader {
constructor() {
this.modules = new Map();
this.loading = new Map();
this.hooks = {
beforeLoad: [],
afterLoad: [],
onError: [],
};
}
// 注册钩子
on(event, callback) {
if (this.hooks[event]) {
this.hooks[event].push(callback);
}
return this;
}
// 触发钩子
async callHook(event, data) {
const callbacks = this.hooks[event] || [];
for (const callback of callbacks) {
await callback(data);
}
}
// 加载模块
async load(url, options = {}) {
const { force = false, timeout = 30000 } = options;
// 检查缓存
if (!force && this.modules.has(url)) {
return this.modules.get(url);
}
// 检查正在加载
if (this.loading.has(url)) {
return this.loading.get(url);
}
// 创建加载 Promise
const loadPromise = this._doLoad(url, timeout);
this.loading.set(url, loadPromise);
try {
const module = await loadPromise;
this.modules.set(url, module);
return module;
} catch (error) {
await this.callHook('onError', { url, error });
throw error;
} finally {
this.loading.delete(url);
}
}
async _doLoad(url, timeout) {
await this.callHook('beforeLoad', { url });
const module = await Promise.race([
import(/* @vite-ignore */ url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(`Load timeout: ${url}`)), timeout)
),
]);
await this.callHook('afterLoad', { url, module });
return module;
}
// 预加载
preload(urls) {
return Promise.all(urls.map(url => this.load(url).catch(() => null)));
}
// 获取已加载模块
get(url) {
return this.modules.get(url);
}
// 清除缓存
clear() {
this.modules.clear();
}
// 获取模块信息
getInfo() {
return {
loaded: Array.from(this.modules.keys()),
loading: Array.from(this.loading.keys()),
};
}
}
// 使用示例
const loader = new ModuleLoader();
loader.on('beforeLoad', ({ url }) => {
console.log(`Loading: ${url}`);
});
loader.on('afterLoad', ({ url, module }) => {
console.log(`Loaded: ${url}`);
});
loader.on('onError', ({ url, error }) => {
console.error(`Failed to load: ${url}`, error);
});
// 加载模块
const module = await loader.load('https://example.com/module.js');模块化应用架构
// src/
// ├── main.js - 入口
// ├── app/
// │ ├── index.js - 应用初始化
// │ ├── router.js - 路由配置
// │ └── store.js - 状态管理
// ├── components/
// │ ├── Button.js
// │ ├── Modal.js
// │ └── index.js
// ├── services/
// │ ├── api.js
// │ ├── auth.js
// │ └── storage.js
// └── utils/
// ├── format.js
// ├── validate.js
// └── index.js
// main.js
import { createApp } from './app/index.js';
import { initRouter } from './app/router.js';
import { initStore } from './app/store.js';
async function bootstrap() {
const app = createApp();
const router = initRouter();
const store = initStore();
app.use(router);
app.use(store);
await router.isReady();
app.mount('#app');
}
bootstrap();
// components/index.js
export { Button } from './Button.js';
export { Modal } from './Modal.js';
export { Card } from './Card.js';
// services/api.js
const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
class ApiService {
constructor() {
this.baseURL = BASE_URL;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers,
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
return response.json();
}
get(endpoint) {
return this.request(endpoint);
}
post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
}
export const api = new ApiService();条件加载与代码分割
// 特性开关控制
const FEATURES = {
newDashboard: import.meta.env.DEV,
analytics: true,
darkMode: true,
};
async function loadFeature(name) {
const config = FEATURES[name];
if (!config) return null;
const path = `./features/${name}/index.js`;
if (typeof config === 'string') {
return import(path);
}
if (config === true) {
return import(path);
}
return null;
}
// 平台检测加载
function getPlatformModule() {
if ('serviceWorker' in navigator) {
return import('./platform/pwa.js');
}
return import('./platform/web.js');
}
// 性能敏感代码分割
class LazyLoader {
#cache = new Map();
async lazy(name, loader) {
if (this.#cache.has(name)) {
return this.#cache.get(name);
}
const module = await loader();
this.#cache.set(name, module);
return module;
}
}
const lazyLoader = new LazyLoader();
// 路由级代码分割
const routes = [
{
path: '/',
component: () => import('./pages/Home.js'),
},
{
path: '/about',
component: () => import('./pages/About.js'),
},
{
path: '/dashboard',
component: () => import('./pages/Dashboard.js'),
children: [
{
path: 'analytics',
component: () => import('./pages/dashboard/Analytics.js'),
},
{
path: 'settings',
component: () => import('./pages/dashboard/Settings.js'),
},
],
},
];性能优化技巧
按需加载优化
// 1. 路由懒加载
// 使用动态 import 实现
const routes = [
{
path: '/users',
component: () => import('./views/Users.vue'),
},
];
// 2. 组件懒加载
const LazyModal = defineAsyncComponent(() =>
import('./components/Modal.vue')
);
// 3. 预加载策略
class SmartPreloader {
constructor() {
this.loaded = new Set();
this.loading = new Set();
}
// 空闲时预加载
preloadOnIdle(urls) {
requestIdleCallback(() => {
urls.forEach(url => this.preload(url));
});
}
// 基于视图预加载
preloadOnView(urls) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const url = entry.target.dataset.preload;
if (url) this.preload(url);
}
});
});
document.querySelectorAll('[data-preload]').forEach(el => {
observer.observe(el);
});
}
async preload(url) {
if (this.loaded.has(url) || this.loading.has(url)) return;
this.loading.add(url);
await import(/* webpackPreload: true */ url);
this.loaded.add(url);
this.loading.delete(url);
}
}模块缓存策略
// 1. Service Worker 缓存
const CACHE_NAME = 'module-cache-v1';
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('.js')) {
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return cache.match(event.request).then(response => {
const fetchPromise = fetch(event.request).then(networkResponse => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return response || fetchPromise;
});
})
);
}
});
// 2. 模块图缓存
class ModuleGraph {
constructor() {
this.graph = new Map();
}
addModule(url, { dependencies, exports }) {
this.graph.set(url, {
url,
dependencies,
exports,
timestamp: Date.now(),
});
}
getDependencies(url) {
const module = this.graph.get(url);
return module ? module.dependencies : [];
}
isStale(url, maxAge = 60000) {
const module = this.graph.get(url);
if (!module) return true;
return Date.now() - module.timestamp > maxAge;
}
}
// 3. 依赖预分析
function analyzeDependencies(entryModule) {
const deps = new Set();
function traverse(module) {
const modules = parseImports(module);
modules.forEach(m => {
if (!deps.has(m)) {
deps.add(m);
traverse(m);
}
});
}
traverse(entryModule);
return Array.from(deps);
}打包优化建议
// 1. Tree Shaking 友好写法
// ✅ 好:使用具名导出
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// ❌ 差:使用默认导出混合
export default {
add,
subtract,
};
// 2. sideEffects 配置
// package.json
{
"sideEffects": [
"*.css",
"./polyfills.js"
]
}
// 3. 避免循环依赖
// 使用共享模块模式
// shared/
// ├── constants.js
// ├── types.js
// └── utils.js
// 不要让模块 A 和 B 相互导入,而是通过 shared 模块
// 4. 动态导入与代码分割
// Webpack/Vite 会自动分割 dynamic import
const heavyModule = await import('./heavy-module.js');与同类技术对比
CommonJS vs ESM vs AMD
| 特性 | CommonJS | ES Modules | AMD |
|---|---|---|---|
| 语法 | require(), module.exports | import, export | define() |
| 加载 | 同步 | 异步 | 异步 |
| 运行环境 | Node.js | 浏览器 + Node.js | 浏览器 |
| 静态分析 | 受限 | 完全支持 | 受限 |
| Tree Shaking | 不支持 | 完全支持 | 部分支持 |
| 循环引用 | 容易出错 | 更好的处理 | 需要回调 |
| 顶 await | 不支持 | 支持 | 不支持 |
| 实时绑定 | 值拷贝 | 支持 | N/A |
| 动态导入 | require() 可动态 | import() 动态 | require() 动态 |
代码对比
// CommonJS
// math.js
const PI = 3.14159;
function add(a, b) {
return a + b;
}
module.exports = {
PI,
add,
};
// main.js
const { PI, add } = require('./math.js');
console.log(add(PI, 2));
// ES Modules
// math.mjs
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default { PI, add };
// main.mjs
import { PI, add } from './math.mjs';
import math from './math.mjs';
console.log(add(PI, 2));
// AMD
// math.js
define(['require', 'exports'], function(require, exports) {
const PI = 3.14159;
exports.add = function(a, b) {
return a + b;
};
});
// main.js
define(['./math'], function(math) {
console.log(math.add(1, 2));
});UMD 模式
// UMD (Universal Module Definition)
// 同时支持 CommonJS、AMD 和全局变量
(function(root, factory) {
// 检测环境
if (typeof module === 'object' && module.exports) {
// CommonJS
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
// AMD
define(factory);
} else {
// 全局变量
root.MyModule = factory();
}
}(typeof self !== 'undefined' ? self : this, function() {
// 模块代码
return {
method1: function() {},
method2: function() {},
};
}));现代替代方案
ESM 已成为标准
- 浏览器原生支持 ES Modules
- Node.js 14+ 支持 ESM (
package.json中"type": "module") - 构建工具(Webpack, Vite, Rollup)优先处理 ESM
- TypeScript 原生输出 ESM
何时使用 CommonJS
- 遗留代码维护
- 某些 npm 包仅提供 CJS 版本
- Node.js 特定场景(如
__dirname)
常见问题与解决方案
问题 1: 模块解析失败
// 问题:Cannot find module './utils'
// 原因:
// 1. 路径错误
// 2. 缺少扩展名
// 3. package.json 配置错误
// 解决方案
// 1. 使用完整路径
import { utils } from './utils.js';
import { utils } from './utils/index.js';
// 2. 配置 package.json
{
"exports": {
".": "./dist/index.js"
}
}
// 3. 使用 import maps
{
"imports": {
"utils": "./src/utils/index.js"
}
}
// 4. 配置构建工具
// vite.config.js
export default {
resolve: {
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'],
},
};问题 2: 循环依赖导致 undefined
// 问题:导入的值是 undefined
// a.js
import { b } from './b.js';
export const a = 'A';
console.log(b); // undefined
// 解决方案
// 1. 使用函数延迟访问
export function getB() {
return b;
}
// 2. 重构设计,避免循环
// 提取共享模块到单独文件
// 3. 使用 export default
// 导出函数或对象
// 4. 延迟初始化
let b;
export function setB(value) {
b = value;
}问题 3: 跨域 CORS 错误
// 问题:Cross-Origin Request Blocked
// 原因:模块文件不在同源或没有正确的 CORS 头
// 解决方案
// 1. 服务端设置 CORS 头
// Nginx 配置
// location ~* \.m?js$ {
// add_header Access-Control-Allow-Origin *;
// }
// 2. 使用本地服务器
// npx serve .
// python -m http.server
// 3. 配置开发服务器代理
// vite.config.js
export default {
server: {
proxy: {
'/api': 'http://localhost:3000',
},
},
};
// 4. 使用 data: URL(仅开发环境)
// import dataUrl from 'data:text/javascript;base64,ZXhwb3J0IGNvbnN0IGE9J0EnOw==';问题 4: 默认导出 vs 命名导出混用
// 问题:默认导出与命名导出混淆
// 解决方案:统一约定
// 方案 A:全部使用命名导出
export const foo = 1;
export function bar() {}
// 方案 B:混合使用(库推荐)
export const VERSION = '1.0.0';
export const helpers = { /* ... */ };
export default App;
// 方案 C:重新导出统一
// index.js
export { default } from './App.js';
export * from './utils.js';问题 5: 模块在 HTML 中的加载顺序
// 问题:模块加载顺序不确定
// 原因:ESM 异步加载
// 解决方案
// 1. 使用 defer 属性
<script type="module" src="app.js" defer></script>
// 所有模块按顺序执行
// 2. 内联模块确保顺序
<script type="module">
import { a } from './a.js';
import { b } from './b.js';
// a 一定在 b 之前
</script>
// 3. 使用动态导入控制顺序
await import('./a.js');
await import('./b.js');问题 6: package.json type 字段
// 问题:Node.js 将 .js 文件当作 CommonJS 还是 ESM
// 解决方案:明确设置
// package.json
{
"type": "module" // 所有 .js 文件当作 ESM
// 或不设置,默认 CommonJS
}
// 文件命名约定
// ESM: .mjs 或 package.json 设置 "type": "module"
// CJS: .cjs
// 混合使用
// ESM 文件中使用 CJS
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const cjsModule = require('./cjs-module.cjs');
// CJS 文件中使用 ESM
// 不支持,需要转译或使用 .mjs问题 7: 动态导入与静态导入混用
// 问题:混淆静态 import 和动态 import()
// 静态导入 - 顶部声明
import { utils } from './utils.js';
import { api } from './api.js';
// 动态导入 - 条件/延迟加载
async function loadFeature() {
if (needsFeature()) {
const { feature } = await import('./feature.js');
return feature;
}
}
// 最佳实践
// 1. 静态导入用于始终需要的模块
// 2. 动态导入用于按需加载的模块
// 3. 不要在静态导入中使用变量
// ❌ import(`./${variable}`) - 错误
// ✅ const module = await import(variablePath);问题 8: 模块类型检测
// 问题:如何检测代码运行在模块环境
// 解决方案
// 1. 使用 import.meta
const isModule = typeof import.meta !== 'undefined';
const moduleURL = import.meta.url;
// 2. 检测 exports
const isESM = typeof exports !== 'undefined' && typeof module !== 'undefined';
// 3. 浏览器检测
const isBrowserModule = document.readyState === 'loading';
// 4. 构建时替换
// rollup-plugin-replace
// __ENV__ === 'module' ? true : false// 4. 构建时替换 // rollup-plugin-replace // ENV === ‘module’ ? true : false
---
## 深度实战案例
### 完整的模块化应用架构
```javascript
// === 项目结构 ===
// src/
// ├── main.js - 入口文件
// ├── app/
// │ ├── index.js - 应用初始化
// │ ├── router.js - 路由管理
// │ ├── store.js - 状态管理
// │ └── ioc.js - 依赖注入容器
// ├── core/
// │ ├── logger.js - 日志系统
// │ ├── config.js - 配置管理
// │ ├── error-handler.js - 错误处理
// │ └── event-bus.js - 事件总线
// ├── services/
// │ ├── api.js - API 服务
// │ ├── auth.js - 认证服务
// │ ├── storage.js - 存储服务
// │ └── analytics.js - 分析服务
// ├── components/
// │ ├── button/
// │ │ ├── Button.js
// │ │ ├── Button.css
// │ │ └── index.js
// │ ├── modal/
// │ │ ├── Modal.js
// │ │ ├── Modal.css
// │ │ └── index.js
// │ └── index.js - 统一导出
// ├── hooks/
// │ ├── useState.js
// │ ├── useEffect.js
// │ ├── useContext.js
// │ └── index.js
// ├── utils/
// │ ├── format.js
// │ ├── validate.js
// │ ├── date.js
// │ └── index.js
// └── types/
// ├── api.js
// ├── user.js
// └── index.js
// === main.js ===
import { createApp } from './app/index.js';
import { initRouter } from './app/router.js';
import { initStore } from './app/store.js';
import { Container } from './app/ioc.js';
import { Logger } from './core/logger.js';
import { ErrorHandler } from './core/error-handler.js';
async function bootstrap() {
// 初始化日志
const logger = new Logger({
level: import.meta.env.DEV ? 'debug' : 'warn',
format: 'json'
});
// 初始化错误处理
const errorHandler = new ErrorHandler(logger);
errorHandler.setup();
// 初始化依赖注入容器
const container = new Container();
// 注册服务
container.register('logger', Logger, { singleton: true });
container.register('api', () => import('./services/api.js'), { singleton: true });
container.register('auth', () => import('./services/auth.js'), { singleton: true });
// 创建应用
const app = createApp({
container,
logger,
errorHandler
});
// 初始化路由
const router = initRouter();
app.use(router);
// 初始化状态管理
const store = initStore();
app.use(store);
// 等待路由准备就绪
await router.isReady();
// 挂载应用
app.mount('#app');
logger.info('Application started', {
version: import.meta.env.VERSION,
mode: import.meta.env.MODE
});
}
bootstrap().catch(console.error);
依赖注入容器实现
// === core/ioc.js ===
class Container {
constructor() {
this.services = new Map();
this.instances = new Map();
}
// 注册服务
register(name, factory, options = {}) {
const { singleton = false, dependencies = [] } = options;
this.services.set(name, {
factory,
singleton,
dependencies,
instance: null
});
}
// 获取服务
get(name) {
const service = this.services.get(name);
if (!service) {
throw new Error(`Service not found: ${name}`);
}
// 单例模式:返回缓存实例
if (service.singleton) {
if (!service.instance) {
service.instance = this.createInstance(service);
}
return service.instance;
}
// 原型模式:每次创建新实例
return this.createInstance(service);
}
// 创建实例
createInstance(service) {
const { factory, dependencies } = service;
// 处理异步工厂
if (factory instanceof Promise) {
throw new Error('Use getAsync for async factories');
}
// 处理函数工厂
if (typeof factory === 'function') {
// 解析依赖
const deps = dependencies.map(dep => {
if (typeof dep === 'string') {
return this.get(dep);
}
return dep;
});
// 检查是否需要注入容器
const injectContainer = factory.length === 0 && deps.length > 0;
if (injectContainer) {
return factory(...deps);
}
return factory(...deps);
}
// 直接返回值
return factory;
}
// 异步获取服务
async getAsync(name) {
const service = this.services.get(name);
if (!service) {
throw new Error(`Service not found: ${name}`);
}
// 异步工厂
if (service.factory instanceof Promise) {
if (service.singleton && service.instance) {
return service.instance;
}
const instance = await service.factory;
if (service.singleton) {
service.instance = instance;
}
return instance;
}
return this.get(name);
}
// 清除所有实例
clear() {
this.instances.clear();
this.services.forEach(service => {
service.instance = null;
});
}
// 获取所有已注册服务
getRegisteredServices() {
return Array.from(this.services.keys());
}
}
// 使用示例
const container = new Container();
// 注册服务
container.register('config', {
apiUrl: import.meta.env.VITE_API_URL,
timeout: 5000
});
container.register('storage', localStorage);
container.register('logger', Logger, {
dependencies: ['config']
});
container.register('api', async () => {
const { default: APIClient } = await import('./api/client.js');
return new APIClient(container.get('config'));
}, { singleton: true });
// 获取服务
const logger = container.get('logger');
const api = await container.getAsync('api');模块级联加载器
// === core/module-loader.js ===
class ModuleLoader {
constructor() {
this.modules = new Map();
this.loaders = new Map();
this.hooks = {
beforeLoad: [],
afterLoad: [],
onError: []
};
}
// 注册模块加载器
register(name, loader) {
this.loaders.set(name, loader);
}
// 添加钩子
on(event, callback) {
if (this.hooks[event]) {
this.hooks[event].push(callback);
}
return () => {
this.hooks[event] = this.hooks[event].filter(cb => cb !== callback);
};
}
// 触发钩子
async callHook(event, data) {
const callbacks = this.hooks[event] || [];
for (const callback of callbacks) {
await callback(data);
}
}
// 加载模块
async load(name, options = {}) {
const { force = false, params = {} } = options;
// 检查缓存
if (!force && this.modules.has(name)) {
return this.modules.get(name);
}
// 检查加载器
const loader = this.loaders.get(name);
if (!loader) {
throw new Error(`No loader registered for: ${name}`);
}
// 触发 beforeLoad 钩子
await this.callHook('beforeLoad', { name, params });
try {
const module = await loader(params);
// 缓存模块
this.modules.set(name, module);
// 触发 afterLoad 钩子
await this.callHook('afterLoad', { name, module });
return module;
} catch (error) {
// 触发 onError 钩子
await this.callHook('onError', { name, error });
throw error;
}
}
// 预加载模块
async preload(names) {
return Promise.all(
names.map(name => this.load(name).catch(() => null))
);
}
// 懒加载模块
lazy(name, params = {}) {
return {
loaded: false,
module: null,
async get() {
if (!this.loaded) {
this.module = await moduleLoader.load(name, { params });
this.loaded = true;
}
return this.module;
}
};
}
// 清除缓存
clear() {
this.modules.clear();
}
// 获取模块信息
getInfo() {
return {
loaded: Array.from(this.modules.keys()),
loaders: Array.from(this.loaders.keys())
};
}
}
// 使用示例
const moduleLoader = new ModuleLoader();
// 注册模块加载器
moduleLoader.register('feature-a', async () => {
const [{ FeatureA }, css] = await Promise.all([
import('./features/FeatureA.js'),
import('./features/FeatureA.css')
]);
return { component: FeatureA, styles: css.default };
});
moduleLoader.register('feature-b', async () => {
const [{ FeatureB }, css] = await Promise.all([
import('./features/FeatureB.js'),
import('./features/FeatureB.css')
]);
return { component: FeatureB, styles: css.default };
});
// 添加钩子
moduleLoader.on('beforeLoad', ({ name }) => {
console.log(`Loading: ${name}`);
});
moduleLoader.on('afterLoad', ({ name, module }) => {
console.log(`Loaded: ${name}`);
registerComponent(module.component);
});
moduleLoader.on('onError', ({ name, error }) => {
console.error(`Failed to load: ${name}`, error);
reportError(error);
});
// 加载模块
const featureA = await moduleLoader.load('feature-a');完整的模块化路由系统
// === app/router.js ===
class Router {
constructor(options = {}) {
this.routes = new Map();
this.currentRoute = null;
this.currentParams = {};
this.beforeGuards = [];
this.afterGuards = [];
this.base = options.base || '/';
}
// 添加路由
addRoute(path, config) {
const { component, children, meta = {}, beforeEnter = null } = config;
this.routes.set(path, {
path,
component,
children,
meta,
beforeEnter,
lazy: typeof component === 'function'
});
// 递归添加子路由
if (children) {
children.forEach(child => {
this.addRoute(`${path}/${child.path}`, {
...child,
parent: path
});
});
}
}
// 添加导航守卫
beforeEach(guard) {
this.beforeGuards.push(guard);
return () => {
this.beforeGuards = this.beforeGuards.filter(g => g !== guard);
};
}
afterEach(guard) {
this.afterGuards.push(guard);
return () => {
this.afterGuards = this.afterGuards.filter(g => g !== guard);
};
}
// 导航到路由
async navigate(path, options = {}) {
const { replace = false } = options;
// 解析路径
const { route, params } = this.matchRoute(path);
if (!route) {
throw new Error(`Route not found: ${path}`);
}
// 执行导航守卫
for (const guard of this.beforeGuards) {
const result = await guard({
from: this.currentRoute,
to: route,
params: this.currentParams,
next: params
});
if (result === false) {
return false; // 导航被阻止
}
if (result && typeof result === 'string') {
return this.navigate(result, options); // 重定向
}
}
// 更新当前路由
const from = this.currentRoute;
this.currentRoute = route;
this.currentParams = params;
// 加载组件(如果是懒加载)
let component = route.component;
if (route.lazy && typeof component === 'function') {
component = await component();
}
// 执行 after 守卫
for (const guard of this.afterGuards) {
await guard({ from, to: route });
}
// 更新 URL
if (replace) {
history.replaceState(null, '', path);
} else {
history.pushState(null, '', path);
}
// 触发路由变化事件
window.dispatchEvent(new CustomEvent('routechange', {
detail: { route, params, component }
}));
return true;
}
// 匹配路由
matchRoute(path) {
for (const [routePath, route] of this.routes) {
const params = this.extractParams(routePath, path);
if (params !== null) {
return { route, params };
}
}
return { route: null, params: {} };
}
// 提取参数
extractParams(routePath, path) {
const routeParts = routePath.split('/').filter(Boolean);
const pathParts = path.split('/').filter(Boolean);
if (routeParts.length !== pathParts.length) {
return null;
}
const params = {};
for (let i = 0; i < routeParts.length; i++) {
if (routeParts[i].startsWith(':')) {
params[routeParts[i].slice(1)] = pathParts[i];
} else if (routeParts[i] !== pathParts[i]) {
return null;
}
}
return params;
}
// 启动路由
setupListeners() {
window.addEventListener('popstate', () => {
this.navigate(window.location.pathname);
});
// 处理点击链接
document.addEventListener('click', (e) => {
const link = e.target.closest('a[href]');
if (link && link.href.startsWith(window.location.origin)) {
e.preventDefault();
this.navigate(link.getAttribute('href'));
}
});
}
// 等待路由就绪
async isReady() {
this.setupListeners();
await this.navigate(window.location.pathname);
}
}
// 路由定义
function createRouter() {
const router = new Router({ base: import.meta.env.BASE_URL });
// 懒加载路由组件
router.addRoute('/', {
component: () => import('./pages/Home.js'),
meta: { title: 'Home' }
});
router.addRoute('/about', {
component: () => import('./pages/About.js'),
meta: { title: 'About' }
});
router.addRoute('/users', {
component: () => import('./pages/Users.js'),
children: [
{ path: '', component: () => import('./pages/users/List.js') },
{ path: ':id', component: () => import('./pages/users/Detail.js') }
]
});
// 添加导航守卫
router.beforeEach(async ({ to, next }) => {
if (to.meta.requiresAuth) {
const auth = await import('./services/auth.js');
if (!auth.isAuthenticated()) {
return '/login';
}
}
return true;
});
return router;
}
export { createRouter };高级模块化模式
微前端架构实现
// === 微前端主应用 ===
class MicroFrontend {
constructor() {
this.apps = new Map();
this.activeApp = null;
}
// 注册微应用
register(name, config) {
const {
entry,
container,
activeRule,
props = {}
} = config;
this.apps.set(name, {
name,
entry,
container,
activeRule,
props,
instance: null,
status: 'NOT_LOADED'
});
}
// 加载微应用
async loadApp(name) {
const app = this.apps.get(name);
if (!app) {
throw new Error(`App not found: ${name}`);
}
if (app.status === 'LOADED') {
return app.instance;
}
app.status = 'LOADING';
try {
// 加载远程模块
const container = document.getElementById(app.container);
const { bootstrap, mount, unmount, update } = await import(/* @vite-ignore */ app.entry);
app.instance = {
name,
bootstrap,
mount,
unmount,
update,
container
};
// 初始化应用
await bootstrap(app.props);
app.status = 'LOADED';
return app.instance;
} catch (error) {
app.status = 'ERROR';
throw error;
}
}
// 挂载微应用
async mountApp(name) {
const app = this.apps.get(name);
if (!app) return;
// 卸载当前应用
if (this.activeApp && this.activeApp !== app) {
await this.unmountApp(this.activeApp.name);
}
// 加载并挂载
const instance = await this.loadApp(name);
await instance.mount(app.container, app.props);
this.activeApp = app;
app.status = 'MOUNTED';
}
// 卸载微应用
async unmountApp(name) {
const app = this.apps.get(name);
if (!app || !app.instance) return;
await app.instance.unmount();
app.status = 'UNMOUNTED';
this.activeApp = null;
}
// 检查活跃规则
checkActiveRule() {
for (const [name, app] of this.apps) {
if (typeof app.activeRule === 'function' && app.activeRule()) {
return name;
}
}
return null;
}
// 全局事件处理
setupGlobalCommunication() {
// 监听来自微应用的消息
window.addEventListener('message', (event) => {
const { type, payload } = event.data;
switch (type) {
case 'NAVIGATE':
history.pushState(null, '', payload.path);
break;
case 'LOG':
console.log(`[${event.source}]`, payload.message);
break;
case 'STATE_UPDATE':
// 更新全局状态
break;
}
});
}
}
// 使用示例
const micro = new MicroFrontend();
// 注册微应用
micro.register('react-app', {
entry: 'http://localhost:3001/assets/index.js',
container: 'react-root',
activeRule: () => window.location.pathname.startsWith('/react'),
props: {
baseUrl: 'http://localhost:3001',
token: getAuthToken()
}
});
micro.register('vue-app', {
entry: 'http://localhost:3002/assets/index.js',
container: 'vue-root',
activeRule: () => window.location.pathname.startsWith('/vue'),
props: {
baseUrl: 'http://localhost:3002',
token: getAuthToken()
}
});
// 监听路由变化
window.addEventListener('popstate', async () => {
const activeApp = micro.checkActiveRule();
if (activeApp) {
await micro.mountApp(activeApp);
}
});
// 初始化
micro.setupGlobalCommunication();
const activeApp = micro.checkActiveRule();
if (activeApp) {
await micro.mountApp(activeApp);
}模块化插件系统
// === 插件系统 ===
class PluginSystem {
constructor() {
this.plugins = new Map();
this.hooks = new Map();
this.context = {};
}
// 注册插件
use(plugin) {
if (typeof plugin === 'function') {
plugin = plugin(this.context);
}
if (!plugin || !plugin.install) {
throw new Error('Invalid plugin');
}
plugin.install(this.context, this);
this.plugins.set(plugin.name, plugin);
return this;
}
// 添加钩子
registerHook(name, callback, options = {}) {
const { before = null, after = null, once = false } = options;
if (!this.hooks.has(name)) {
this.hooks.set(name, []);
}
const hooks = this.hooks.get(name);
const hook = { callback, before, after, once, called: false };
if (before) {
hooks.unshift(hook);
} else if (after) {
hooks.push(hook);
} else {
hooks.push(hook);
}
return () => {
const hooks = this.hooks.get(name);
const index = hooks.indexOf(hook);
if (index > -1) hooks.splice(index, 1);
};
}
// 调用钩子
async callHook(name, ...args) {
const hooks = this.hooks.get(name);
if (!hooks) return;
let result;
for (const hook of hooks) {
if (hook.once && hook.called) continue;
if (hook.before) {
await hook.before(...args);
}
result = await hook.callback(...args);
hook.called = true;
if (hook.after) {
await hook.after(result, ...args);
}
}
return result;
}
// 设置上下文
setContext(context) {
this.context = { ...this.context, ...context };
}
// 获取所有插件
getPlugins() {
return Array.from(this.plugins.values());
}
// 卸载插件
uninstall(name) {
const plugin = this.plugins.get(name);
if (plugin && plugin.uninstall) {
plugin.uninstall(this.context);
}
this.plugins.delete(name);
}
}
// 创建示例插件
function createLoggerPlugin() {
return {
name: 'logger',
install(context, system) {
console.log('Logger plugin installed');
system.registerHook('beforeRender', () => {
console.log('Before render');
});
system.registerHook('afterRender', (result) => {
console.log('After render:', result);
});
},
uninstall(context) {
console.log('Logger plugin uninstalled');
}
};
}
function createAnalyticsPlugin() {
return {
name: 'analytics',
install(context, system) {
system.registerHook('afterRender', (result) => {
// 发送分析数据
trackPageView(window.location.pathname);
});
}
};
}
// 使用示例
const app = new PluginSystem();
app.setContext({
version: '1.0.0',
env: import.meta.env.MODE
});
app.use(createLoggerPlugin());
app.use(createAnalyticsPlugin());
// 使用钩子
await app.callHook('beforeRender');
const result = await app.callHook('render');
await app.callHook('afterRender', result);渐进式模块加载策略
// === 渐进式加载器 ===
class ProgressiveLoader {
constructor(options = {}) {
this.priority = options.priority || ['critical', 'high', 'medium', 'low'];
this.loadedModules = new Set();
this.loadingModules = new Map();
this.queue = {
critical: [],
high: [],
medium: [],
low: []
};
this.maxConcurrent = options.maxConcurrent || 3;
this.currentLoading = 0;
}
// 添加模块到加载队列
add(module, priority = 'medium') {
if (this.loadedModules.has(module.path) || this.loadingModules.has(module.path)) {
return;
}
this.queue[priority].push(module);
if (priority === 'critical' || priority === 'high') {
this.processQueue();
}
}
// 处理加载队列
async processQueue() {
if (this.currentLoading >= this.maxConcurrent) return;
for (const priority of this.priority) {
while (this.queue[priority].length > 0 && this.currentLoading < this.maxConcurrent) {
const module = this.queue[priority].shift();
await this.loadModule(module);
}
}
// 加载完成后处理剩余队列
if (this.hasPendingModules()) {
requestIdleCallback(() => this.processQueue());
}
}
// 加载单个模块
async loadModule(module) {
const { path, loader } = module;
this.loadingModules.set(path, true);
this.currentLoading++;
try {
const result = await loader();
this.loadedModules.add(path);
this.loadingModules.delete(path);
module.onLoad?.(result);
} catch (error) {
module.onError?.(error);
} finally {
this.currentLoading--;
this.processQueue();
}
}
// 检查是否有待加载模块
hasPendingModules() {
return Object.values(this.queue).some(q => q.length > 0) ||
this.loadingModules.size > 0;
}
// 预加载模块
preload(paths, priority = 'medium') {
paths.forEach(path => {
this.add({
path,
loader: () => import(/* webpackPrefetch: true */ path)
}, priority);
});
}
// 批量加载
async loadAll(paths, priority = 'medium') {
const modules = paths.map(path => ({
path,
loader: () => import(path)
}));
await Promise.all(
modules.map(module => this.add(module, priority))
);
}
}
// 使用示例
const loader = new ProgressiveLoader({
priority: ['critical', 'high', 'medium', 'low'],
maxConcurrent: 3
});
// 关键模块 - 立即加载
loader.add({
path: 'core/app',
loader: () => import('./core/app.js'),
onLoad: (app) => app.init(),
onError: (err) => reportCriticalError(err)
}, 'critical');
// 高优先级模块
loader.add({
path: 'core/router',
loader: () => import('./core/router.js'),
onLoad: (router) => router.setup()
}, 'high');
// 中等优先级 - 按需加载
loader.add({
path: 'features/dashboard',
loader: () => import('./features/dashboard.js')
}, 'medium');
// 低优先级 - 空闲时加载
loader.add({
path: 'features/analytics',
loader: () => import('./features/analytics.js'),
onLoad: () => initAnalytics()
}, 'low');
// 预加载用户可能需要的模块
loader.preload([
'./features/profile.js',
'./features/settings.js'
], 'low');模块系统最佳实践
模块设计原则
// === 单一职责原则 ===
// 差的设计:一个模块做太多事情
// user-module.js
export function createUser(data) { /* 创建用户 */ }
export function updateUser(id, data) { /* 更新用户 */ }
export function deleteUser(id) { /* 删除用户 */ }
export function listUsers() { /* 列出用户 */ }
export function sendEmail(user) { /* 发送邮件 */ }
export function validateEmail(email) { /* 验证邮箱 */ }
export function formatDate(date) { /* 格式化日期 */ }
// 好的设计:职责分明
// user/service.js - 业务逻辑
export class UserService {
constructor(repository, emailService) {
this.repository = repository;
this.emailService = emailService;
}
async create(data) {
const user = await this.repository.create(data);
await this.emailService.sendWelcome(user);
return user;
}
}
// user/repository.js - 数据访问
export class UserRepository {
async create(data) { /* ... */ }
async update(id, data) { /* ... */ }
async delete(id) { /* ... */ }
async findById(id) { /* ... */ }
}
// user/email-service.js - 邮件服务
export class EmailService {
async sendWelcome(user) { /* ... */ }
}
// user/validators.js - 验证器
export function validateUserData(data) { /* ... */ }// === 依赖倒置原则 ===
// 差的写法:直接依赖具体实现
import { MySQLDatabase } from './database/mysql.js';
import { RedisCache } from './cache/redis.js';
class UserService {
constructor() {
this.db = new MySQLDatabase();
this.cache = new RedisCache();
}
}
// 好的写法:依赖抽象接口
// interfaces.js
export class IDatabase {
async query(sql) { throw new Error('Not implemented'); }
async insert(table, data) { throw new Error('Not implemented'); }
}
export class ICache {
async get(key) { throw new Error('Not implemented'); }
async set(key, value) { throw new Error('Not implemented'); }
}
// user-service.js
class UserService {
constructor(database, cache) {
this.database = database;
this.cache = cache;
}
}
// 在应用入口处注入具体实现
import { MySQLDatabase } from './database/mysql.js';
import { RedisCache } from './cache/redis.js';
const db = new MySQLDatabase(config);
const cache = new RedisCache(config);
const userService = new UserService(db, cache);模块化代码组织模式
// === 桶模式 (Barrel Pattern) ===
// components/
// ├── index.js (桶文件 - 导出所有组件)
// ├── Button/
// │ ├── Button.js
// │ ├── Button.css
// │ └── index.js
// ├── Modal/
// │ ├── Modal.js
// │ ├── Modal.css
// │ └── index.js
// └── Card/
// ├── Card.js
// ├── Card.css
// └── index.js
// components/Button/index.js
export { Button } from './Button.js';
export { ButtonGroup } from './ButtonGroup.js';
// components/Modal/index.js
export { Modal } from './Modal.js';
export { ModalHeader } from './ModalHeader.js';
export { ModalBody } from './ModalBody.js';
export { ModalFooter } from './ModalFooter.js';
// components/Card/index.js
export { Card } from './Card.js';
export { CardHeader } from './CardHeader.js';
export { CardContent } from './CardContent.js';
// components/index.js (桶文件)
export { Button, ButtonGroup } from './Button/index.js';
export { Modal, ModalHeader, ModalBody, ModalFooter } from './Modal/index.js';
export { Card, CardHeader, CardContent } from './Card/index.js';
// 使用方式
// 好的方式:从桶导入
import { Button, Modal, Card } from './components';
// 不好的方式:从具体文件导入
import { Button } from './components/Button/Button.js';
import { Modal } from './components/Modal/Modal.js';// === 领域驱动模块组织 ===
// src/
// ├── domains/
// │ ├── users/
// │ │ ├── domain/ # 领域逻辑
// │ │ │ ├── entities/
// │ │ │ │ └── User.js
// │ │ │ ├── value-objects/
// │ │ │ │ └── Email.js
// │ │ │ ├── services/
// │ │ │ │ └── UserService.js
// │ │ │ └── events/
// │ │ │ └── UserEvents.js
// │ │ ├── application/ # 应用逻辑
// │ │ │ ├── use-cases/
// │ │ │ │ ├── CreateUser.js
// │ │ │ │ └── UpdateUser.js
// │ │ │ └── dto/
// │ │ │ └── UserDTO.js
// │ │ ├── infrastructure/ # 基础设施
// │ │ │ ├── repositories/
// │ │ │ │ └── UserRepository.js
// │ │ │ └── mappers/
// │ │ │ └── UserMapper.js
// │ │ └── index.js # 公开接口
// │ └── orders/
// │ └── ...
// ├── shared/
// │ ├── kernel/ # 共享内核
// │ │ ├── Entity.js
// │ │ ├── ValueObject.js
// │ │ └── DomainEvent.js
// │ ├── utils/
// │ └── constants/
// └── app/
// └── services/
// domains/users/domain/value-objects/Email.js
export class Email {
constructor(value) {
if (!this.isValid(value)) {
throw new Error(`Invalid email: ${value}`);
}
this.value = value;
}
isValid(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
equals(other) {
return other instanceof Email && this.value === other.value;
}
}
// domains/users/domain/entities/User.js
import { Entity } from '../../../shared/kernel/Entity.js';
export class User extends Entity {
constructor(props) {
super(props.id);
this.email = props.email;
this.name = props.name;
this.createdAt = props.createdAt;
}
updateName(name) {
this.name = name;
this.addDomainEvent('UserNameUpdated', { userId: this.id, name });
}
}与其他技术对比(续)
Webpack 模块系统 vs Vite
| 特性 | Webpack | Vite |
|---|---|---|
| 开发服务器 | DevServer | 原生 ESM |
| 热更新 | WebSocket + 替换 | 原生 HMR API |
| 构建速度 | 较慢(整包打包) | 快速(按需编译) |
| 生产构建 | webpack | Rollup |
| 配置复杂度 | 高 | 低 |
| 插件生态 | 丰富 | 正在发展 |
| 兼容性 | 好 | 依赖原生 ESM |
// Webpack 配置示例
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash].js',
path: '/dist',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
new HtmlWebpackPlugin(),
new DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
// Vite 配置示例
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:3001'
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia']
}
}
}
}
});ES Modules vs Web Components 模块化
| 方面 | ES Modules | Web Components |
|---|---|---|
| 作用域 | 模块级 | Shadow DOM |
| 导入方式 | import/export | 自定义元素 |
| 样式封装 | 无 | Shadow DOM |
| 生命周期 | 无 | connectedCallback 等 |
| 适用场景 | 逻辑复用 | UI 组件 |
| 框架依赖 | 无 | 无 |
// Web Component 示例
// my-component.js
class MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
padding: 16px;
background: white;
border-radius: 8px;
}
</style>
<div class="content">
<slot></slot>
</div>
`;
}
}
customElements.define('my-component', MyComponent);
// 在 HTML 中使用
// <my-component>
// <h2>Hello</h2>
// </my-component>常见问题与解决方案(续)
问题 9: 模块作用域冲突
// 问题:多个模块定义同名变量
// module-a.js
const config = { apiUrl: 'http://localhost:3000' };
// module-b.js
const config = { apiUrl: 'http://localhost:4000' };
// 使用时出现混淆
// 解决方案 1:命名空间
export const moduleA = { config: { apiUrl: 'http://localhost:3000' } };
export const moduleB = { config: { apiUrl: 'http://localhost:4000' } };
// 使用
import { moduleA, moduleB } from './modules.js';
moduleA.config.apiUrl;
moduleB.config.apiUrl;
// 解决方案 2:IIFE 隔离
const ModuleA = (function() {
const config = { apiUrl: 'http://localhost:3000' };
return { config };
})();问题 10: 模块懒加载与预加载冲突
// 问题:预加载的模块被重复加载
const modulePromise = import('./heavy-module.js'); // 预加载
// 稍后使用
const module = await import('./heavy-module.js'); // 重新加载?
// 解决方案:使用单例模式
class LazyLoader {
static #cache = new Map();
static async load(path) {
if (this.#cache.has(path)) {
return this.#cache.get(path);
}
const promise = import(path);
this.#cache.set(path, promise);
return promise;
}
}
// 预加载
LazyLoader.load('./heavy-module.js'); // 开始加载,缓存 Promise
// 稍后使用
const module = await LazyLoader.load('./heavy-module.js'); // 使用缓存的 Promise问题 11: 模块类型兼容
// 问题:第三方模块没有类型定义
// 解决方案 1:声明文件
// types/my-module.d.ts
declare module 'my-module' {
export function myFunction(arg: string): Promise<Result>;
export class MyClass {
constructor(options: Options);
method(): void;
}
}
// 解决方案 2:运行时类型检查
import { validate } from 'my-module';
function safeCall(module, method, ...args) {
if (typeof module[method] === 'function') {
return module[method](...args);
}
throw new Error(`Method ${method} not found`);
}
// 解决方案 3:使用 ts-auto-guard问题 12: 大型模块树的构建优化
// 问题:大型应用中模块树过于庞大
// 解决方案:代码分割策略
// 1. 按路由分割
const routes = [
{
path: '/dashboard',
component: () => import('./pages/Dashboard.js') // 懒加载
},
{
path: '/settings',
component: () => import('./pages/Settings.js')
}
];
// 2. 按功能分割
const features = {
chart: () => import('./features/chart.js'),
editor: () => import('./features/editor.js'),
pdf: () => import('./features/pdf.js')
};
// 3. 组件级别分割
const HeavyComponent = React.lazy(() => import('./HeavyComponent.js'));
// 4. 条件分割
if (condition) {
const { heavyModule } = await import('./heavy.js');
}问题 13: 模块化与 SSR 兼容性
// 问题:ES Modules 在 SSR 环境中使用
// 解决方案:适配层
// ssr-adapter.js
let fetchModule;
if (typeof window !== 'undefined') {
// 浏览器环境
fetchModule = (path) => import(/* @vite-ignore */ path);
} else {
// Node.js 环境
fetchModule = (path) => import(fileURLToPath(path));
}
// 使用
export async function loadModule(path) {
return fetchModule(path);
}
// 通用模块加载器
export async function loadComponent(name) {
const path = getComponentPath(name);
return loadModule(path);
}模块系统高级模式
惰性加载与代码分割
// 惰性加载模块
class LazyLoader {
#cache = new Map();
async load(path, options = {}) {
const { force = false } = options;
if (!force && this.#cache.has(path)) {
return this.#cache.get(path);
}
const module = await import(/* webpackChunkName: "[request]" */ path);
this.#cache.set(path, module);
return module;
}
// 预加载
preload(path) {
import(/* webpackPrefload: true */ path);
}
// 懒加载包装
lazy(loader) {
let promise = null;
return () => {
if (!promise) {
promise = loader().then(module => {
this.#cache.set(loader, module);
return module;
});
}
return promise;
};
}
}
// 使用示例
const loader = new LazyLoader();
// 路由懒加载
const routes = [
{
path: '/dashboard',
component: () => loader.load('./pages/Dashboard.js')
},
{
path: '/settings',
component: () => loader.load('./pages/Settings.js')
}
];
// 条件懒加载
async function loadFeature(feature) {
if (feature === 'editor') {
const { Editor } = await loader.load('./features/Editor.js');
return new Editor();
}
if (feature === 'charts') {
const { Charts } = await loader.load('./features/Charts.js');
return new Charts();
}
}模块懒加载与预取策略
// 预取管理器
class PrefetchManager {
constructor() {
this.observer = null;
this.prefetchLinks = new Set();
this.setupIntersectionObserver();
}
setupIntersectionObserver() {
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const link = entry.target;
this.prefetch(link.dataset.prefetch);
this.observer.unobserve(link);
}
});
},
{ rootMargin: '200px' }
);
}
observe(element) {
this.observer.observe(element);
}
async prefetch(path) {
if (this.prefetchLinks.has(path)) return;
this.prefetchLinks.add(path);
try {
await import(/* webpackPrefetch: true */ path);
} catch (error) {
console.warn(`Prefetch failed for ${path}:`, error);
}
}
// 基于预测的预取
prefetchOnHover(event) {
const link = event.target.closest('a[href]');
if (link && link.href.startsWith('/')) {
const path = this.resolvePath(link.href);
this.prefetch(path);
}
}
resolvePath(href) {
// 转换 URL 到模块路径
return `./pages${href}.js`;
}
destroy() {
this.observer.disconnect();
}
}
// 路由预取
const routerPrefetcher = new PrefetchManager();
document.addEventListener('mouseover', (e) => {
routerPrefetcher.prefetchOnHover(e);
}, { passive: true });
// 自动预取可见链接
document.querySelectorAll('[data-prefetch]').forEach((el) => {
routerPrefetcher.observe(el);
});模块热替换(HMR)
// 模块热替换示例
let currentModule = null;
async function loadModuleWithHMR(modulePath) {
try {
const newModule = await import(/* webpackHotUpdate: true */ modulePath);
if (currentModule) {
// 调用热替换 API
if (module.hot) {
module.hot.accept();
// 执行热替换回调
if (currentModule.onHMRUpdate) {
currentModule.onHMRUpdate(newModule);
}
}
// 清理旧模块
if (currentModule.onHMRDispose) {
currentModule.onHMRDispose();
}
}
currentModule = newModule;
return newModule;
} catch (error) {
console.error('Hot reload failed:', error);
return currentModule;
}
}
// Vite HMR API
// main.js
if (import.meta.hot) {
import.meta.hot.on('vite:beforeUpdate', (data) => {
console.log('Updating:', data.modules);
});
import.meta.hot.on('vite:afterUpdate', (data) => {
console.log('Updated:', data.modules);
});
import.meta.hot.dispose((data) => {
// 清理资源
data.cleanup = () => {
console.log('Cleanup from HMR');
};
});
import.meta.hot.on('vite:error', (error) => {
console.error('HMR Error:', error);
});
}模块化状态管理
// 模块化状态管理
class ModuleStore {
constructor() {
this.modules = new Map();
this.listeners = new Map();
}
register(name, store) {
this.modules.set(name, {
state: store.state,
actions: store.actions,
mutations: store.mutations,
getters: store.getters
});
// 初始化 getters
if (store.getters) {
Object.keys(store.getters).forEach(key => {
store.getters[key] = store.getters[key](store.state);
});
}
}
getState(name) {
const module = this.modules.get(name);
return module ? { ...module.state } : null;
}
getAllState() {
const state = {};
this.modules.forEach((module, name) => {
state[name] = { ...module.state };
});
return state;
}
dispatch(moduleName, actionName, payload) {
const module = this.modules.get(moduleName);
if (!module || !module.actions[actionName]) {
throw new Error(`Action not found: ${moduleName}/${actionName}`);
}
const context = {
state: module.state,
commit: (mutation, payload) => this.commit(moduleName, mutation, payload),
dispatch: (m, a, p) => this.dispatch(m, a, p),
getters: module.getters
};
return module.actions[actionName](context, payload);
}
commit(moduleName, mutationName, payload) {
const module = this.modules.get(moduleName);
if (!module || !module.mutations[mutationName]) {
throw new Error(`Mutation not found: ${moduleName}/${mutationName}`);
}
module.mutations[mutationName](module.state, payload);
this.notify(moduleName);
}
subscribe(callback) {
const id = Symbol('subscription');
this.listeners.set(id, callback);
return () => this.listeners.delete(id);
}
notify(moduleName) {
this.listeners.forEach(callback => {
callback({
type: moduleName,
state: this.getState(moduleName)
});
});
}
}
// 使用示例
const store = new ModuleStore();
// 注册 user 模块
store.register('user', {
state: {
name: '',
email: '',
isLoggedIn: false
},
getters: {
displayName: (state) => state.name || state.email
},
mutations: {
SET_USER(state, user) {
state.name = user.name;
state.email = user.email;
state.isLoggedIn = true;
},
LOGOUT(state) {
state.name = '';
state.email = '';
state.isLoggedIn = false;
}
},
actions: {
async login({ commit }, credentials) {
const user = await api.login(credentials);
commit('SET_USER', user);
},
logout({ commit }) {
commit('LOGOUT');
}
}
});
// 订阅状态变化
store.subscribe(({ type, state }) => {
console.log(`Module ${type} changed:`, state);
});模块化国际化(i18n)
// 模块化国际化系统
class I18n {
constructor(options = {}) {
this.locale = options.locale || 'en';
this.fallbackLocale = options.fallbackLocale || 'en';
this.modules = new Map();
this.formatters = {};
}
registerModule(name, translations) {
this.modules.set(name, translations);
}
async loadModule(name, loader) {
const translations = await loader();
this.registerModule(name, translations);
}
t(key, params = {}) {
const keys = key.split('.');
let value = null;
// 先在当前语言查找
for (const module of this.modules.values()) {
value = this.getNestedValue(module, keys);
if (value) break;
}
// 回退到默认语言
if (!value && this.fallbackLocale !== this.locale) {
const fallback = this.modules.get(this.fallbackLocale);
if (fallback) {
value = this.getNestedValue(fallback, keys);
}
}
if (!value) {
console.warn(`Missing translation: ${key}`);
return key;
}
// 替换参数
return this.interpolate(value, params);
}
getNestedValue(obj, keys) {
let current = obj;
for (const key of keys) {
if (current && typeof current === 'object' && key in current) {
current = current[key];
} else {
return null;
}
}
return current;
}
interpolate(text, params) {
return text.replace(/\{\{(\w+)\}\}/g, (match, key) => {
return params[key] !== undefined ? params[key] : match;
});
}
setLocale(locale) {
this.locale = locale;
// 触发重新渲染
document.documentElement.lang = locale;
}
}
// 使用示例
const i18n = new I18n({ locale: 'en', fallbackLocale: 'en' });
// 注册模块
i18n.registerModule('common', {
en: {
welcome: 'Welcome',
logout: 'Logout',
buttons: {
submit: 'Submit',
cancel: 'Cancel'
}
},
zh: {
welcome: '欢迎',
logout: '退出登录',
buttons: {
submit: '提交',
cancel: '取消'
}
}
});
i18n.registerModule('dashboard', {
en: {
title: 'Dashboard',
stats: {
users: '{{count}} users',
revenue: 'Revenue: ${{amount}}'
}
},
zh: {
title: '仪表盘',
stats: {
users: '{{count}} 个用户',
revenue: '收入: ¥{{amount}}'
}
}
});
// 使用翻译
console.log(i18n.t('common.welcome')); // "Welcome"
console.log(i18n.t('common.buttons.submit')); // "Submit"
console.log(i18n.t('dashboard.stats.users', { count: 42 })); // "42 users"模块化验证系统
// 模块化验证器
class Validator {
constructor() {
this.rules = new Map();
this.messages = new Map();
this.registerDefaultMessages();
}
registerDefaultMessages() {
this.messages.set('required', 'This field is required');
this.messages.set('email', 'Please enter a valid email');
this.messages.set('min', 'Value must be at least {{min}}');
this.messages.set('max', 'Value must be at most {{max}}');
this.messages.set('pattern', 'Invalid format');
}
register(name, validator, message) {
this.rules.set(name, validator);
if (message) {
this.messages.set(name, message);
}
}
validate(value, rules) {
const errors = [];
for (const [ruleName, ruleValue] of Object.entries(rules)) {
const validator = this.rules.get(ruleName);
if (!validator) continue;
const isValid = validator(value, ruleValue);
if (!isValid) {
let message = this.messages.get(ruleName) || `${ruleName} validation failed`;
message = message.replace(/{{(\w+)}}/g, (_, key) => ruleValue[key] || ruleValue);
errors.push({ rule: ruleName, message });
}
}
return {
valid: errors.length === 0,
errors
};
}
validateObject(obj, schema) {
const allErrors = {};
for (const [field, rules] of Object.entries(schema)) {
const result = this.validate(obj[field], rules);
if (!result.valid) {
allErrors[field] = result.errors;
}
}
return {
valid: Object.keys(allErrors).length === 0,
errors: allErrors
};
}
}
// 注册自定义验证器
const validator = new Validator();
validator.register('required', (value) => {
if (Array.isArray(value)) return value.length > 0;
return value !== null && value !== undefined && value !== '';
});
validator.register('email', (value) => {
if (!value) return true;
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(value);
});
validator.register('min', (value, min) => {
if (typeof value === 'number') return value >= min;
if (typeof value === 'string') return value.length >= min;
return true;
});
validator.register('max', (value, max) => {
if (typeof value === 'number') return value <= max;
if (typeof value === 'string') return value.length <= max;
return true;
});
validator.register('pattern', (value, regex) => {
if (!value) return true;
return new RegExp(regex).test(value);
});
validator.register('custom', (value, fn) => {
return fn(value);
}, 'Custom validation failed');
// 使用验证器
const result = validator.validateObject(
{
username: 'john',
email: 'john@example.com',
age: 25
},
{
username: { required: true, min: 3, max: 20 },
email: { required: true, email: true },
age: { required: true, min: 18, max: 100 }
}
);
console.log(result);模块化日志系统
// 模块化日志系统
class Logger {
static levels = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3
};
constructor(options = {}) {
this.level = options.level || Logger.levels.INFO;
this.modules = new Map();
this.handlers = options.handlers || [
new ConsoleHandler()
];
}
createModule(name, moduleLevel = null) {
const module = new Logger({
...options,
moduleName: name,
moduleLevel
});
this.modules.set(name, module);
return module;
}
debug(...args) {
this.log(Logger.levels.DEBUG, ...args);
}
info(...args) {
this.log(Logger.levels.INFO, ...args);
}
warn(...args) {
this.log(Logger.levels.WARN, ...args);
}
error(...args) {
this.log(Logger.levels.ERROR, ...args);
}
log(level, ...args) {
if (level < this.level) return;
const entry = {
level,
levelName: Object.keys(Logger.levels).find(k => Logger.levels[k] === level),
message: args,
timestamp: new Date(),
module: this.moduleName
};
this.handlers.forEach(handler => handler(entry));
}
}
class ConsoleHandler {
format(entry) {
const time = entry.timestamp.toISOString();
const prefix = entry.module ? `[${entry.module}]` : '';
return `${time} ${entry.levelName} ${prefix} ${entry.message.join(' ')}`;
}
handle(entry) {
const formatted = this.format(entry);
switch (entry.level) {
case Logger.levels.DEBUG:
case Logger.levels.INFO:
console.log(formatted);
break;
case Logger.levels.WARN:
console.warn(formatted);
break;
case Logger.levels.ERROR:
console.error(formatted);
break;
}
}
}
class FileHandler {
constructor(filename) {
this.filename = filename;
}
handle(entry) {
// 实际会写入文件
const line = JSON.stringify(entry) + '\n';
fs.appendFileSync(this.filename, line);
}
}
class RemoteHandler {
constructor(endpoint) {
this.endpoint = endpoint;
}
async handle(entry) {
try {
await fetch(this.endpoint, {
method: 'POST',
body: JSON.stringify(entry),
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.error('Failed to send log:', error);
}
}
}
// 使用示例
const logger = new Logger({ level: Logger.levels.DEBUG });
const apiLogger = logger.createModule('api', Logger.levels.INFO);
apiLogger.info('API request started');
apiLogger.debug('Request payload:', { id: 123 });
apiLogger.error('API request failed:', new Error('Network error'));
const dbLogger = logger.createModule('database');
dbLogger.info('Query executed', { duration: '45ms' });模块系统调试与诊断
模块解析调试
// 模块解析调试工具
class ModuleResolver {
constructor(options = {}) {
this.baseURL = options.baseURL || process.cwd();
this.trace = options.trace || false;
this.resolutions = new Map();
}
resolve(specifier, parentURL = this.baseURL) {
const cacheKey = `${parentURL}:${specifier}`;
if (this.resolutions.has(cacheKey)) {
if (this.trace) {
console.log(`[Cache Hit] ${specifier} -> ${this.resolutions.get(cacheKey)}`);
}
return this.resolutions.get(cacheKey);
}
if (this.trace) {
console.log(`[Resolving] ${specifier} from ${parentURL}`);
}
let resolved;
try {
resolved = this.doResolve(specifier, parentURL);
this.resolutions.set(cacheKey, resolved);
if (this.trace) {
console.log(`[Resolved] ${specifier} -> ${resolved}`);
}
return resolved;
} catch (error) {
if (this.trace) {
console.error(`[Failed] ${specifier}:`, error.message);
}
throw error;
}
}
doResolve(specifier, parentURL) {
// 实现解析逻辑
if (specifier.startsWith('./') || specifier.startsWith('../')) {
return new URL(specifier, parentURL).pathname;
}
if (specifier.startsWith('/')) {
return specifier;
}
// 裸说明符:查找 node_modules
return this.resolveInNodeModules(specifier, parentURL);
}
resolveInNodeModules(specifier, parentURL) {
let dir = new URL('.', parentURL).pathname;
while (dir !== '/') {
const nodeModulesPath = `${dir}node_modules/${specifier}`;
if (this.fileExists(nodeModulesPath)) {
return nodeModulesPath;
}
dir = dir.replace(/\/[^/]+\/$/, '');
}
throw new Error(`Cannot find module: ${specifier}`);
}
fileExists(path) {
try {
require('fs').accessSync(path);
return true;
} catch {
return false;
}
}
getResolutionMap() {
return Object.fromEntries(this.resolutions);
}
clearCache() {
this.resolutions.clear();
}
}
// 使用示例
const resolver = new ModuleResolver({
baseURL: '/project/src',
trace: true
});
resolver.resolve('./utils/helper.js', '/project/src/app.js');
resolver.resolve('lodash', '/project/src/app.js');循环依赖检测
// 循环依赖检测器
class CircularDependencyDetector {
constructor() {
this.graph = new Map();
this.cycles = [];
}
addModule(name, dependencies) {
this.graph.set(name, dependencies || []);
}
detect() {
const visited = new Set();
const recursionStack = new Set();
const path = [];
const dfs = (moduleName) => {
visited.add(moduleName);
recursionStack.add(moduleName);
path.push(moduleName);
const dependencies = this.graph.get(moduleName) || [];
for (const dep of dependencies) {
if (!visited.has(dep)) {
if (dfs(dep)) {
return true;
}
} else if (recursionStack.has(dep)) {
// 发现循环
const cycleStart = path.indexOf(dep);
this.cycles.push([...path.slice(cycleStart), dep].join(' → '));
}
}
path.pop();
recursionStack.delete(moduleName);
return false;
};
for (const module of this.graph.keys()) {
if (!visited.has(module)) {
dfs(module);
}
}
return this.cycles;
}
getDependencyTree() {
const tree = {};
const buildTree = (name, visited = new Set()) => {
if (visited.has(name)) {
return { _circular: true };
}
visited.add(name);
const deps = this.graph.get(name) || [];
tree[name] = deps.map(dep => buildTree(dep, new Set(visited)));
return { name, dependencies: tree[name] };
};
for (const module of this.graph.keys()) {
if (!tree[module]) {
buildTree(module);
}
}
return tree;
}
}
// 使用示例
const detector = new CircularDependencyDetector();
detector.addModule('a', ['b', 'c']);
detector.addModule('b', ['c']);
detector.addModule('c', ['d']);
detector.addModule('d', ['a']); // 循环依赖
const cycles = detector.detect();
console.log('Circular dependencies found:', cycles);
// ["d → a → b → c → d"]
const tree = detector.getDependencyTree();
console.log('Dependency tree:', JSON.stringify(tree, null, 2));模块性能分析
// 模块加载性能分析器
class ModuleProfiler {
constructor() {
this.modules = new Map();
this.currentModule = null;
this.startTime = null;
}
startProfiling() {
if (typeof globalThis !== 'undefined') {
globalThis.__moduleProfiler = this;
}
}
recordModuleStart(name) {
this.currentModule = name;
this.startTime = performance.now();
this.modules.set(name, {
name,
loadTime: 0,
dependencies: [],
loadedAt: Date.now()
});
}
recordModuleEnd() {
if (this.currentModule && this.startTime) {
const loadTime = performance.now() - this.startTime;
const module = this.modules.get(this.currentModule);
if (module) {
module.loadTime = loadTime;
}
}
this.currentModule = null;
this.startTime = null;
}
recordDependency(moduleName, dependencyName) {
const module = this.modules.get(moduleName);
if (module) {
module.dependencies.push({
name: dependencyName,
loadTime: this.modules.get(dependencyName)?.loadTime || 0
});
}
}
getReport() {
const sorted = Array.from(this.modules.values())
.sort((a, b) => b.loadTime - a.loadTime);
return {
totalModules: this.modules.size,
totalLoadTime: sorted.reduce((sum, m) => sum + m.loadTime, 0),
slowestModules: sorted.slice(0, 10),
moduleDetails: Object.fromEntries(this.modules)
};
}
printReport() {
const report = this.getReport();
console.log('=== Module Load Report ===');
console.log(`Total Modules: ${report.totalModules}`);
console.log(`Total Load Time: ${report.totalLoadTime.toFixed(2)}ms`);
console.log('\nSlowest Modules:');
report.slowestModules.forEach(m => {
console.log(` ${m.name}: ${m.loadTime.toFixed(2)}ms`);
});
}
}
// 使用示例
const profiler = new ModuleProfiler();
profiler.startProfiling();
// 模拟模块加载
profiler.recordModuleStart('main');
profiler.recordModuleStart('utils');
// ... 加载代码 ...
profiler.recordModuleEnd();
profiler.recordModuleStart('api');
profiler.recordModuleEnd();
profiler.recordModuleEnd(); // main
profiler.printReport();模块系统最佳实践
模块设计原则
// === 单一职责原则 ===
// 好的模块设计
// utils/format.js
export function formatDate(date, locale = 'en-US') {
return new Intl.DateTimeFormat(locale).format(date);
}
// utils/validation.js
export function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
// utils/storage.js
export const storage = {
get(key) { return localStorage.getItem(key); },
set(key, value) { localStorage.setItem(key, value); },
remove(key) { localStorage.removeItem(key); }
};
// 差的模块设计
// mixed-utils.js
export function formatDate(date) { /* ... */ }
export function validateEmail(email) { /* ... */ }
export function saveToStorage(data) { /* ... */ }
export function loadFromStorage(key) { /* ... */ }
export function formatCurrency(amount) { /* ... */ }// === 依赖倒置原则 ===
// 差的写法
import { MySQLDatabase } from './database/mysql.js';
import { RedisCache } from './cache/redis.js';
class UserService {
constructor() {
this.db = new MySQLDatabase();
this.cache = new RedisCache();
}
}
// 好的写法
// interfaces/storage.js
export class IStorage {
async get(key) { throw new Error('Not implemented'); }
async set(key, value) { throw new Error('Not implemented'); }
async delete(key) { throw new Error('Not implemented'); }
}
export class ICache {
async get(key) { throw new Error('Not implemented'); }
async set(key, value, ttl) { throw new Error('Not implemented'); }
}
// implementations/mysql-storage.js
export class MySQLStorage extends IStorage {
async get(key) { /* MySQL 实现 */ }
async set(key, value) { /* MySQL 实现 */ }
async delete(key) { /* MySQL 实现 */ }
}
// implementations/redis-cache.js
export class RedisCache extends ICache {
async get(key) { /* Redis 实现 */ }
async set(key, value, ttl) { /* Redis 实现 */ }
}
// services/user-service.js
class UserService {
constructor(storage, cache) {
this.storage = storage;
this.cache = cache;
}
}
// 应用入口
import { MySQLStorage } from './implementations/mysql-storage.js';
import { RedisCache } from './implementations/redis-cache.js';
const storage = new MySQLStorage(config);
const cache = new RedisCache(config);
const userService = new UserService(storage, cache);模块化代码组织模式
// === 桶模式 (Barrel Pattern) ===
// 导出单个组件
// components/Button/index.js
export { Button } from './Button.js';
export { ButtonGroup } from './ButtonGroup.js';
export { ButtonSpinner } from './ButtonSpinner.js';
// 导出 UI 组件
// components/index.js
export * from './Button/index.js';
export * from './Modal/index.js';
export * from './Card/index.js';
export * from './Input/index.js';
// 使用方式
// 好的方式
import { Button, Modal, Card } from '@/components';
// 不好的方式
import { Button } from '@/components/Button/Button.js';
import { Modal } from '@/components/Modal/Modal.js';
// === 按功能组织 ===
// src/
// ├── features/
// │ ├── auth/
// │ │ ├── components/ # 认证相关组件
// │ │ ├── hooks/ # 认证相关 hooks
// │ │ ├── services/ # 认证服务
// │ │ ├── stores/ # 认证状态
// │ │ └── utils/ # 认证工具
// │ │ └── index.js # 桶导出
// │ │
// │ ├── dashboard/
// │ │ └── ...
// │
// ├── shared/
// │ ├── components/ # 共享组件
// │ ├── hooks/ # 共享 hooks
// │ ├── utils/ # 共享工具
// │ └── constants/ # 共享常量
// │
// └── app/
// ├── routes.js
// └── App.js
// features/auth/index.js
export { LoginForm } from './components/LoginForm.js';
export { useAuth } from './hooks/useAuth.js';
export { authService } from './services/authService.js';
export { useAuthStore } from './stores/authStore.js';// === 领域驱动模块组织 ===
// domains/
// ├── users/
// │ ├── domain/
// │ │ ├── entities/
// │ │ │ └── User.js
// │ │ ├── value-objects/
// │ │ │ └── Email.js
// │ │ ├── services/
// │ │ │ └── UserDomainService.js
// │ │ └── events/
// │ │ └── UserEvents.js
// │ │
// │ ├── application/
// │ │ ├── commands/
// │ │ │ └── CreateUserCommand.js
// │ │ ├── queries/
// │ │ │ └── GetUserByIdQuery.js
// │ │ └── handlers/
// │ │ └── UserCommandHandler.js
// │ │
// │ ├── infrastructure/
// │ │ ├── repositories/
// │ │ │ └── UserRepository.js
// │ │ └── mappers/
// │ │ └── UserMapper.js
// │ │
// │ └── index.js # 公开接口
// │
// └── orders/
// └── ...
// domains/users/domain/value-objects/Email.js
export class Email {
#value;
constructor(value) {
if (!this.isValid(value)) {
throw new Error(`Invalid email: ${value}`);
}
this.#value = value.toLowerCase();
}
isValid(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
equals(other) {
return other instanceof Email && this.#value === other.#value;
}
toString() {
return this.#value;
}
}总结
ES Modules 是 JavaScript 模块系统的现代标准,提供了清晰、声明式的导入导出语法。通过理解模块解析算法,开发者可以更好地控制模块的加载行为。循环依赖虽然是应该尽量避免的问题,但通过正确的设计模式(延迟访问、共享模块、工厂函数、事件驱动),即使在复杂系统中也能安全处理。
掌握这些知识,将帮助你构建更加健壮、可维护的前端应用。现代前端开发已经进入模块化时代,从 ES Modules 到微前端,从依赖注入到插件系统,模块化思想贯穿整个技术栈。理解并实践这些模式,将使你的代码更加清晰、可测试、可维护。