模块系统

概述

JavaScript 模块系统是现代前端工程化的基石。从早期的全局变量满天飞,到 CommonJS 的服务器端统治,再到 ES Modules(ESM)成为浏览器和 Node.js 的统一标准,模块系统经历了漫长的演进。本文将全面解析 ES Modules 的完整语法,对比 CommonJS 与 ESM 的差异,深入剖析模块解析算法,并探讨循环依赖的处理策略。

ES Modules 完整语法

import/export 基础

ES Modules 使用 importexport 关键字实现模块的导入导出,支持多种导出导入方式:

// === 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 对比

核心差异

特性CommonJSES Modules
语法require() / module.exportsimport / 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 使用统一的模块解析算法,主要步骤:

  1. 确定模块说明符类型

    • 相对路径(./, ../
    • 绝对路径(/
    • URL(https://...
    • bare specifier(无路径,如 lodash
  2. 应用模块说明符扩展

    • 自动添加 .js, .mjs, .cjs 扩展名
    • 检查目录下的 package.jsonexports 字段
    • 查找 index.js, index.mjs
  3. 符号链接解析

    • 解析 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

特性CommonJSES ModulesAMD
语法require(), module.exportsimport, exportdefine()
加载同步异步异步
运行环境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

特性WebpackVite
开发服务器DevServer原生 ESM
热更新WebSocket + 替换原生 HMR API
构建速度较慢(整包打包)快速(按需编译)
生产构建webpackRollup
配置复杂度
插件生态丰富正在发展
兼容性依赖原生 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 ModulesWeb 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 到微前端,从依赖注入到插件系统,模块化思想贯穿整个技术栈。理解并实践这些模式,将使你的代码更加清晰、可测试、可维护。