工程化工具类:模块化系统全解析与实践

引言

在前端开发的演进历程中,模块化一直是工程化实践的核心。从早期的脚本标签堆砌到现代的ES Modules,模块化技术极大地提升了代码的可维护性、复用性和协作效率。本文将深入探讨模块化的各个方面,包括模块加载器实现、规范演化、Polyfill技术,并补充构建工具、性能优化等工程化实践,全面解析模块化在现代前端开发中的应用。

一、实现简单的模块加载器

在理解复杂模块系统之前,我们先实现一个简单的模块加载器,了解其核心原理。

1.1 基础模块加载器实现
javascript 复制代码
// 简单的模块注册表
const moduleRegistry = {};
const moduleCache = {};

// 模块定义函数
function define(name, dependencies, factory) {
  if (!moduleRegistry[name]) {
    moduleRegistry[name] = {
      dependencies,
      factory,
      resolved: false,
      exports: null
    };
  }
}

// 模块加载函数
function require(name) {
  // 检查缓存
  if (moduleCache[name]) {
    return moduleCache[name];
  }
  
  const module = moduleRegistry[name];
  if (!module) {
    throw new Error(`Module ${name} not found`);
  }
  
  // 解析依赖
  const resolvedDeps = module.dependencies.map(dep => {
    if (dep === 'exports' || dep === 'module') {
      return null; // 特殊处理
    }
    return require(dep);
  });
  
  // 执行工厂函数获取模块导出
  const factoryResult = module.factory.apply(null, resolvedDeps);
  
  // 缓存模块导出
  moduleCache[name] = factoryResult || {};
  module.resolved = true;
  
  return moduleCache[name];
}

// 使用示例
define('math', [], function() {
  return {
    add: (a, b) => a + b,
    multiply: (a, b) => a * b
  };
});

define('calculator', ['math'], function(math) {
  return {
    calculate: (x, y) => math.multiply(math.add(x, y), 2)
  };
});

// 使用模块
const calculator = require('calculator');
console.log(calculator.calculate(2, 3)); // 10
1.2 异步模块加载器
javascript 复制代码
class AsyncModuleLoader {
  constructor() {
    this.modules = new Map();
    this.loading = new Map();
  }
  
  // 定义模块
  define(name, deps, factory) {
    this.modules.set(name, {
      deps,
      factory,
      exports: null,
      resolved: false
    });
  }
  
  // 异步加载模块
  async require(name) {
    if (this.modules.get(name)?.resolved) {
      return this.modules.get(name).exports;
    }
    
    // 防止重复加载
    if (this.loading.has(name)) {
      return this.loading.get(name);
    }
    
    // 创建加载Promise
    const loadPromise = this._loadModule(name);
    this.loading.set(name, loadPromise);
    
    return loadPromise;
  }
  
  async _loadModule(name) {
    const module = this.modules.get(name);
    if (!module) {
      throw new Error(`Module ${name} not found`);
    }
    
    // 加载所有依赖
    const depPromises = module.deps.map(dep => this.require(dep));
    const deps = await Promise.all(depPromises);
    
    // 执行工厂函数
    const exports = module.factory.apply(null, deps);
    
    // 更新模块状态
    module.exports = exports || {};
    module.resolved = true;
    this.loading.delete(name);
    
    return module.exports;
  }
}

// 使用示例
const loader = new AsyncModuleLoader();

loader.define('utils', [], () => ({
  format: str => str.toUpperCase()
}));

loader.define('app', ['utils'], (utils) => {
  return {
    run: () => console.log(utils.format('hello'))
  };
});

loader.require('app').then(app => app.run()); // 输出: HELLO

二、AMD规范实现

AMD(Asynchronous Module Definition)规范是RequireJS推广的异步模块定义标准。

2.1 简化的AMD实现
javascript 复制代码
(function(global) {
  // 模块缓存
  const modules = {};
  const inProgress = {};
  
  // 定义函数
  function define(id, dependencies, factory) {
    if (arguments.length === 2) {
      factory = dependencies;
      dependencies = [];
    }
    
    modules[id] = {
      id: id,
      dependencies: dependencies,
      factory: factory,
      exports: null,
      resolved: false
    };
    
    // 尝试解析模块
    resolveModule(id);
  }
  
  // 依赖解析
  function resolveModule(id) {
    const module = modules[id];
    if (!module || module.resolved) return;
    
    // 检查依赖是否都可用
    const deps = module.dependencies;
    const missingDeps = deps.filter(dep => 
      !modules[dep] || !modules[dep].resolved
    );
    
    if (missingDeps.length === 0) {
      // 所有依赖已就绪,执行工厂函数
      executeModule(id);
    } else {
      // 等待依赖
      missingDeps.forEach(dep => {
        if (!inProgress[dep]) {
          inProgress[dep] = [];
        }
        inProgress[dep].push(id);
      });
    }
  }
  
  // 执行模块
  function executeModule(id) {
    const module = modules[id];
    if (module.resolved) return;
    
    // 获取依赖的exports
    const depExports = module.dependencies.map(dep => {
      if (dep === 'exports') return {};
      if (dep === 'require') return createRequire();
      if (dep === 'module') return { id: module.id, exports: {} };
      return modules[dep].exports;
    });
    
    // 执行工厂函数
    const exports = module.factory.apply(null, depExports);
    
    // 设置exports
    module.exports = exports || 
      (depExports[module.dependencies.indexOf('exports')] || {});
    module.resolved = true;
    
    // 通知等待此模块的其他模块
    if (inProgress[id]) {
      inProgress[id].forEach(dependentId => resolveModule(dependentId));
      delete inProgress[id];
    }
  }
  
  // 创建require函数
  function createRequire() {
    return function(ids, callback) {
      if (typeof ids === 'string') ids = [ids];
      
      Promise.all(ids.map(loadModule))
        .then(modules => {
          if (callback) callback.apply(null, modules);
        });
    };
  }
  
  // 异步加载模块
  function loadModule(id) {
    return new Promise((resolve, reject) => {
      if (modules[id] && modules[id].resolved) {
        resolve(modules[id].exports);
        return;
      }
      
      // 动态加载脚本
      const script = document.createElement('script');
      script.src = id + '.js';
      script.onload = () => {
        // 等待模块解析
        const checkInterval = setInterval(() => {
          if (modules[id] && modules[id].resolved) {
            clearInterval(checkInterval);
            resolve(modules[id].exports);
          }
        }, 10);
      };
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }
  
  // 暴露到全局
  global.define = define;
  global.require = createRequire();
  
})(typeof window !== 'undefined' ? window : global);

// 使用示例
define('math', [], function() {
  return {
    add: function(a, b) { return a + b; }
  };
});

define('app', ['math', 'require'], function(math, require) {
  return {
    calculate: function() {
      return math.add(1, 2);
    },
    loadExtra: function() {
      require(['utils'], function(utils) {
        console.log('Utils loaded');
      });
    }
  };
});

require(['app'], function(app) {
  console.log(app.calculate()); // 3
});

三、CMD规范实现

CMD(Common Module Definition)规范由Sea.js推广,强调就近依赖。

3.1 简化的CMD实现
javascript 复制代码
(function(global) {
  const modules = {};
  const factories = {};
  const cache = {};
  
  // 模块状态
  const STATUS = {
    PENDING: 0,
    LOADING: 1,
    LOADED: 2,
    EXECUTING: 3,
    EXECUTED: 4
  };
  
  // 定义函数
  function define(factory) {
    // 获取当前脚本
    const scripts = document.getElementsByTagName('script');
    const currentScript = scripts[scripts.length - 1];
    const id = currentScript.src.replace(/\.js$/, '');
    
    factories[id] = factory;
    modules[id] = {
      id: id,
      factory: factory,
      deps: [],
      exports: null,
      status: STATUS.PENDING,
      callbacks: []
    };
    
    // 解析依赖
    parseDependencies(id);
  }
  
  // 解析依赖
  function parseDependencies(id) {
    const factory = factories[id];
    if (!factory) return;
    
    const source = factory.toString();
    const requireRegex = /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
    const deps = [];
    let match;
    
    while ((match = requireRegex.exec(source)) !== null) {
      deps.push(match[1]);
    }
    
    modules[id].deps = deps;
  }
  
  // 异步加载模块
  function require(id, callback) {
    const module = modules[id];
    
    if (module && module.status === STATUS.EXECUTED) {
      // 模块已执行,直接返回
      if (callback) {
        callback(module.exports);
      }
      return module.exports;
    }
    
    // 模块未加载,开始加载
    if (!module || module.status === STATUS.PENDING) {
      return loadModule(id, callback);
    }
    
    // 模块加载中,添加回调
    if (module.status < STATUS.EXECUTED) {
      module.callbacks.push(callback);
    }
  }
  
  // 加载模块
  function loadModule(id, callback) {
    const module = modules[id] || (modules[id] = {
      id: id,
      deps: [],
      exports: null,
      status: STATUS.LOADING,
      callbacks: callback ? [callback] : []
    });
    
    // 创建script标签加载
    const script = document.createElement('script');
    script.src = id + '.js';
    script.async = true;
    
    script.onload = function() {
      module.status = STATUS.LOADED;
      executeModule(id);
    };
    
    script.onerror = function() {
      console.error(`Failed to load module: ${id}`);
    };
    
    document.head.appendChild(script);
    
    return null;
  }
  
  // 执行模块
  function executeModule(id) {
    const module = modules[id];
    if (!module || module.status >= STATUS.EXECUTING) return;
    
    module.status = STATUS.EXECUTING;
    
    // 收集依赖
    const deps = module.deps;
    const depValues = deps.map(depId => {
      const depModule = modules[depId];
      if (depModule && depModule.status === STATUS.EXECUTED) {
        return depModule.exports;
      }
      // 同步加载依赖(简化实现)
      return require(depId);
    });
    
    // 执行工厂函数
    const factory = factories[id];
    if (!factory) {
      throw new Error(`Factory not found for module: ${id}`);
    }
    
    // 提供require、exports、module参数
    const localRequire = function(depId) {
      return require(depId);
    };
    
    const localExports = {};
    const localModule = { exports: localExports };
    
    // 执行
    const result = factory.call(null, localRequire, localExports, localModule);
    
    // 设置exports
    module.exports = localModule.exports || result || localExports;
    module.status = STATUS.EXECUTED;
    
    // 执行回调
    module.callbacks.forEach(cb => cb(module.exports));
    module.callbacks = [];
  }
  
  // 暴露全局
  global.define = define;
  global.require = require;
  
})(typeof window !== 'undefined' ? window : global);

// 使用示例
// 文件: math.js
define(function(require, exports, module) {
  module.exports = {
    add: function(a, b) {
      return a + b;
    }
  };
});

// 文件: app.js
define(function(require, exports, module) {
  var math = require('math');
  
  exports.calculate = function() {
    return math.add(1, 2);
  };
});

// 主文件
require('app', function(app) {
  console.log(app.calculate()); // 3
});

四、ES Module的简单Polyfill

虽然现代浏览器支持ES Modules,但在某些场景下,我们仍需要Polyfill支持。

4.1 基础ESM Polyfill实现
javascript 复制代码
// ES Module Polyfill
(function() {
  const moduleMap = new Map();
  const moduleCache = new Map();
  
  // 拦截import语句(通过动态import实现)
  window.importModule = async function(modulePath) {
    // 检查缓存
    if (moduleCache.has(modulePath)) {
      return moduleCache.get(modulePath);
    }
    
    // 加载模块代码
    const code = await fetchModule(modulePath);
    
    // 解析依赖
    const deps = extractDependencies(code);
    
    // 加载依赖
    const depPromises = deps.map(dep => 
      importModule(resolvePath(modulePath, dep))
    );
    const dependencies = await Promise.all(depPromises);
    
    // 执行模块
    const moduleExports = {};
    const module = {
      exports: moduleExports
    };
    
    // 创建包装函数
    const wrapper = createWrapper(code, dependencies);
    wrapper(
      moduleExports, // exports
      module,        // module
      modulePath     // __filename(模拟)
    );
    
    // 缓存结果
    const exports = module.exports === moduleExports ? 
      moduleExports : module.exports;
    moduleCache.set(modulePath, exports);
    
    return exports;
  };
  
  // 提取依赖
  function extractDependencies(code) {
    const importRegex = /import\s+.*?\s+from\s+['"](.*?)['"]/g;
    const dynamicImportRegex = /import\s*\(['"](.*?)['"]\)/g;
    const deps = new Set();
    
    let match;
    while ((match = importRegex.exec(code)) !== null) {
      deps.add(match[1]);
    }
    
    // 重置正则
    importRegex.lastIndex = 0;
    
    while ((match = dynamicImportRegex.exec(code)) !== null) {
      deps.add(match[1]);
    }
    
    return Array.from(deps);
  }
  
  // 创建包装函数
  function createWrapper(code, dependencies) {
    const wrapperCode = `
      (function(exports, module, __filename, __dirname) {
        // 注入依赖
        const [
          ${dependencies.map((_, i) => `__dep${i}`).join(', ')}
        ] = arguments[4];
        
        ${code}
        
        // 返回默认导出
        return module.exports && module.exports.default ?
          module.exports.default : module.exports;
      })
    `;
    
    return eval(wrapperCode);
  }
  
  // 解析路径
  function resolvePath(basePath, targetPath) {
    if (targetPath.startsWith('./') || targetPath.startsWith('../')) {
      const baseDir = basePath.substring(0, basePath.lastIndexOf('/'));
      return new URL(targetPath, baseDir + '/').pathname;
    }
    return targetPath;
  }
  
  // 获取模块代码
  async function fetchModule(path) {
    const response = await fetch(path);
    if (!response.ok) {
      throw new Error(`Failed to load module: ${path}`);
    }
    return response.text();
  }
  
  // 拦截script type="module"
  interceptModuleScripts();
  
  function interceptModuleScripts() {
    const originalCreateElement = document.createElement;
    
    document.createElement = function(tagName) {
      const element = originalCreateElement.call(document, tagName);
      
      if (tagName === 'script') {
        const originalSetAttribute = element.setAttribute.bind(element);
        
        element.setAttribute = function(name, value) {
          originalSetAttribute(name, value);
          
          if (name === 'type' && value === 'module') {
            // 拦截模块脚本
            const src = element.getAttribute('src');
            if (src) {
              element.type = 'text/javascript';
              importModule(src).then(() => {
                if (element.onload) element.onload();
              }).catch(err => {
                if (element.onerror) element.onerror(err);
              });
            }
          }
        };
      }
      
      return element;
    };
  }
})();

// 使用示例
// 模块文件: utils.js
export function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export default function greet(name) {
  return `Hello, ${capitalize(name)}!`;
}

// 主文件
importModule('./utils.js').then(utils => {
  console.log(utils.default('world')); // Hello, World!
  console.log(utils.capitalize('test')); // Test
});
4.2 支持Tree Shaking的ESM Polyfill
javascript 复制代码
class ESMCompat {
  constructor() {
    this.modules = new Map();
    this.usedExports = new Set();
  }
  
  // 注册模块
  register(name, code) {
    const ast = this.parse(code);
    const exports = this.extractExports(ast);
    
    this.modules.set(name, {
      code,
      ast,
      exports,
      used: new Set()
    });
  }
  
  // 解析代码为AST(简化版)
  parse(code) {
    // 简化实现:实际应使用Babel等解析器
    const exportMatches = code.match(/export\s+(const|let|var|function|class|default)\s+(\w+)/g) || [];
    const imports = code.match(/import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/g) || [];
    
    return {
      exports: exportMatches.map(match => ({
        type: match.split(' ')[1],
        name: match.split(' ')[2]
      })),
      imports: imports.map(match => {
        const parts = match.match(/import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/);
        return {
          specifiers: parts[1].split(',').map(s => s.trim()),
          source: parts[2]
        };
      })
    };
  }
  
  // 提取导出
  extractExports(ast) {
    return ast.exports.map(exp => exp.name);
  }
  
  // 使用模块(标记使用的导出)
  use(name, ...exports) {
    const module = this.modules.get(name);
    if (module) {
      exports.forEach(exp => {
        if (module.exports.includes(exp)) {
          module.used.add(exp);
        }
      });
    }
  }
  
  // 生成优化后的代码
  generateOptimized(name) {
    const module = this.modules.get(name);
    if (!module) return '';
    
    let code = module.code;
    
    // 移除未使用的导出(简化实现)
    module.exports.forEach(exp => {
      if (!module.used.has(exp)) {
        const regex = new RegExp(`export\\s+.*?\\b${exp}\\b[^;]*;`, 'g');
        code = code.replace(regex, '');
      }
    });
    
    return code;
  }
}

// 使用示例
const compat = new ESMCompat();

compat.register('math', `
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
export function unusedFunction() { return 'unused'; }
`);

// 标记使用的导出
compat.use('math', 'PI', 'add');

// 生成优化代码
console.log(compat.generateOptimized('math'));
// 输出将只包含PI和add的导出

五、模块化构建工具集成

现代开发中,我们使用构建工具处理模块化。以下展示如何集成Webpack-like的简单打包器。

5.1 简易模块打包器
javascript 复制代码
const fs = require('fs');
const path = require('path');
const { parse } = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generate = require('@babel/generator').default;
const t = require('@babel/types');

class SimpleBundler {
  constructor(entry) {
    this.entry = entry;
    this.modules = new Map();
    this.moduleId = 0;
  }
  
  // 构建
  build(outputPath) {
    const entryModule = this.collectDependencies(this.entry);
    const bundleCode = this.generateBundle(entryModule);
    
    fs.writeFileSync(outputPath, bundleCode);
    console.log(`Bundle generated: ${outputPath}`);
  }
  
  // 收集依赖
  collectDependencies(filePath) {
    const fileContent = fs.readFileSync(filePath, 'utf-8');
    const ast = parse(fileContent, {
      sourceType: 'module',
      plugins: ['jsx']
    });
    
    const dependencies = [];
    const dirname = path.dirname(filePath);
    
    // 遍历AST收集import语句
    traverse(ast, {
      ImportDeclaration: ({ node }) => {
        const importPath = node.source.value;
        const absolutePath = this.resolvePath(importPath, dirname);
        dependencies.push(absolutePath);
      },
      CallExpression: ({ node }) => {
        if (node.callee.type === 'Import') {
          const importPath = node.arguments[0].value;
          const absolutePath = this.resolvePath(importPath, dirname);
          dependencies.push(absolutePath);
        }
      }
    });
    
    const moduleId = this.moduleId++;
    const module = {
      id: moduleId,
      filePath,
      code: fileContent,
      dependencies,
      mapping: {}
    };
    
    this.modules.set(filePath, module);
    
    // 递归收集依赖
    dependencies.forEach(dep => {
      if (!this.modules.has(dep)) {
        this.collectDependencies(dep);
      }
    });
    
    return module;
  }
  
  // 解析路径
  resolvePath(importPath, baseDir) {
    if (importPath.startsWith('.')) {
      return path.resolve(baseDir, importPath);
    }
    // 处理node_modules(简化)
    const nodeModulePath = path.resolve(process.cwd(), 'node_modules', importPath);
    if (fs.existsSync(nodeModulePath)) {
      return nodeModulePath;
    }
    return importPath;
  }
  
  // 生成打包代码
  generateBundle(entryModule) {
    const modules = [];
    
    // 创建模块映射
    this.modules.forEach(module => {
      const transformedCode = this.transformModule(module);
      modules.push(`
        ${module.id}: {
          factory: function(require, module, exports) {
            ${transformedCode}
          },
          mapping: ${JSON.stringify(module.mapping)}
        }
      `);
    });
    
    // 生成运行时
    return `
      (function(modules) {
        const moduleCache = {};
        
        function require(id) {
          if (moduleCache[id]) {
            return moduleCache[id].exports;
          }
          
          const mod = modules[id];
          const localRequire = function(modulePath) {
            return require(mod.mapping[modulePath]);
          };
          
          const module = { exports: {} };
          mod.factory(localRequire, module, module.exports);
          
          moduleCache[id] = module;
          return module.exports;
        }
        
        // 启动入口模块
        require(0);
      })({
        ${modules.join(',\n')}
      });
    `;
  }
  
  // 转换模块代码
  transformModule(module) {
    const ast = parse(module.code, {
      sourceType: 'module'
    });
    
    // 构建路径映射
    let importIndex = 0;
    
    traverse(ast, {
      ImportDeclaration: ({ node }) => {
        const importPath = node.source.value;
        const depModule = this.modules.get(
          this.resolvePath(importPath, path.dirname(module.filePath))
        );
        
        if (depModule) {
          const importName = `__import_${importIndex++}`;
          module.mapping[importPath] = depModule.id;
          
          // 替换import语句
          const specifiers = node.specifiers.map(spec => {
            if (t.isImportDefaultSpecifier(spec)) {
              return t.variableDeclarator(
                spec.local,
                t.memberExpression(
                  t.identifier(importName),
                  t.identifier('default')
                )
              );
            } else {
              return t.variableDeclarator(
                spec.local,
                t.memberExpression(
                  t.identifier(importName),
                  spec.imported || spec.local
                )
              );
            }
          });
          
          return t.variableDeclaration('const', specifiers);
        }
      }
    });
    
    // 移除export语句
    traverse(ast, {
      ExportNamedDeclaration: ({ node, remove }) => {
        if (node.declaration) {
          return node.declaration;
        }
        remove();
      },
      ExportDefaultDeclaration: ({ node }) => {
        return t.expressionStatement(
          t.assignmentExpression(
            '=',
            t.memberExpression(
              t.identifier('module'),
              t.identifier('exports')
            ),
            t.objectExpression([
              t.objectProperty(
                t.identifier('default'),
                node.declaration
              )
            ])
          )
        );
      }
    });
    
    const { code } = generate(ast);
    return code;
  }
}

// 使用示例
const bundler = new SimpleBundler('./src/index.js');
bundler.build('./dist/bundle.js');

六、模块联邦与微前端架构

模块联邦(Module Federation)是Webpack 5引入的重要特性,支持跨应用共享模块。

6.1 简易模块联邦实现
javascript 复制代码
// 模块联邦管理器
class ModuleFederation {
  constructor(config) {
    this.config = config;
    this.remotes = new Map();
    this.exposes = new Map();
    this.shared = new Map();
    this.init();
  }
  
  init() {
    // 初始化共享模块
    if (this.config.shared) {
      Object.entries(this.config.shared).forEach(([name, config]) => {
        this.shared.set(name, {
          module: require(name),
          version: config.version,
          singleton: config.singleton || false
        });
      });
    }
    
    // 初始化暴露模块
    if (this.config.exposes) {
      Object.entries(this.config.exposes).forEach(([name, modulePath]) => {
        this.exposes.set(name, require(modulePath));
      });
    }
  }
  
  // 注册远程应用
  async registerRemote(name, url) {
    try {
      const remoteManifest = await this.fetchRemoteManifest(url);
      this.remotes.set(name, {
        url,
        manifest: remoteManifest
      });
      console.log(`Remote ${name} registered`);
    } catch (error) {
      console.error(`Failed to register remote ${name}:`, error);
    }
  }
  
  // 获取远程清单
  async fetchRemoteManifest(url) {
    const response = await fetch(`${url}/federation-manifest.json`);
    return response.json();
  }
  
  // 获取模块
  async getModule(remoteName, moduleName) {
    // 检查共享模块
    if (this.shared.has(moduleName)) {
      return this.shared.get(moduleName).module;
    }
    
    // 检查本地暴露
    if (this.exposes.has(moduleName)) {
      return this.exposes.get(moduleName);
    }
    
    // 检查远程模块
    const remote = this.remotes.get(remoteName);
    if (remote) {
      return this.loadRemoteModule(remote, moduleName);
    }
    
    throw new Error(`Module ${moduleName} not found`);
  }
  
  // 加载远程模块
  async loadRemoteModule(remote, moduleName) {
    const moduleUrl = `${remote.url}/${moduleName}.js`;
    
    // 动态加载脚本
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = moduleUrl;
      
      script.onload = () => {
        // 假设远程模块会暴露到全局
        const module = window[`${remote.name}_${moduleName}`];
        if (module) {
          resolve(module);
        } else {
          reject(new Error(`Module ${moduleName} not found in remote`));
        }
      };
      
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }
  
  // 暴露模块
  expose(name, module) {
    this.exposes.set(name, module);
    // 暴露到全局(供远程访问)
    window[`${this.config.name}_${name}`] = module;
  }
}

// 使用示例
// App 1 配置
const federation1 = new ModuleFederation({
  name: 'app1',
  exposes: {
    './Button': './src/components/Button.js'
  },
  shared: {
    react: { singleton: true, version: '17.0.0' },
    'react-dom': { singleton: true, version: '17.0.0' }
  }
});

// App 2 配置
const federation2 = new ModuleFederation({
  name: 'app2',
  remotes: {
    app1: 'http://localhost:3001'
  },
  shared: {
    react: { singleton: true, version: '17.0.0' }
  }
});

// App2中使用App1的模块
federation2.getModule('app1', 'Button').then(Button => {
  // 使用远程Button组件
  console.log('Remote Button loaded:', Button);
});
七、模块化性能优化
7.1 代码分割与懒加载
javascript 复制代码
class CodeSplitter {
  constructor() {
    this.chunks = new Map();
    this.loadedChunks = new Set();
  }
  
  // 定义代码分割点
  defineChunk(name, getChunk) {
    this.chunks.set(name, getChunk);
  }
  
  // 懒加载代码块
  async loadChunk(name) {
    if (this.loadedChunks.has(name)) {
      return;
    }
    
    const getChunk = this.chunks.get(name);
    if (!getChunk) {
      throw new Error(`Chunk ${name} not found`);
    }
    
    // 标记为加载中
    this.loadedChunks.add(name);
    
    try {
      await getChunk();
      console.log(`Chunk ${name} loaded`);
    } catch (error) {
      this.loadedChunks.delete(name);
      throw error;
    }
  }
  
  // 预加载代码块
  preloadChunk(name) {
    if (this.loadedChunks.has(name)) return;
    
    const link = document.createElement('link');
    link.rel = 'preload';
    link.as = 'script';
    
    const getChunk = this.chunks.get(name);
    if (getChunk && getChunk.chunkPath) {
      link.href = getChunk.chunkPath;
      document.head.appendChild(link);
    }
  }
}

// Webpack动态导入兼容
function dynamicImport(modulePath) {
  if (typeof __webpack_require__ !== 'undefined') {
    // Webpack环境
    return import(/* webpackChunkName: "[request]" */ modulePath);
  } else {
    // 原生环境
    return import(modulePath);
  }
}

// 使用示例
const splitter = new CodeSplitter();

// 定义代码块
splitter.defineChunk('dashboard', () => 
  dynamicImport('./Dashboard.js')
);

splitter.defineChunk('analytics', () => 
  dynamicImport('./Analytics.js')
);

// 路由懒加载
async function loadRoute(routeName) {
  switch (routeName) {
    case 'dashboard':
      await splitter.loadChunk('dashboard');
      break;
    case 'analytics':
      await splitter.loadChunk('analytics');
      break;
  }
}

// 预加载
window.addEventListener('mouseover', (e) => {
  if (e.target.href && e.target.href.includes('dashboard')) {
    splitter.preloadChunk('dashboard');
  }
});
7.2 模块缓存策略
javascript 复制代码
class ModuleCache {
  constructor() {
    this.cache = new Map();
    this.ttl = 5 * 60 * 1000; // 5分钟
    this.maxSize = 100; // 最大缓存模块数
  }
  
  // 获取模块
  async get(key, fetchModule) {
    const cached = this.cache.get(key);
    
    // 检查缓存是否有效
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      console.log(`Cache hit: ${key}`);
      return cached.module;
    }
    
    // 缓存失效或不存在,重新获取
    console.log(`Cache miss: ${key}`);
    const module = await fetchModule();
    
    // 更新缓存
    this.set(key, module);
    
    return module;
  }
  
  // 设置缓存
  set(key, module) {
    // 清理过期缓存
    this.cleanup();
    
    this.cache.set(key, {
      module,
      timestamp: Date.now()
    });
  }
  
  // 清理缓存
  cleanup() {
    const now = Date.now();
    
    // 清理过期
    for (const [key, value] of this.cache) {
      if (now - value.timestamp > this.ttl) {
        this.cache.delete(key);
      }
    }
    
    // 清理超出大小限制的(LRU策略)
    if (this.cache.size > this.maxSize) {
      const entries = Array.from(this.cache.entries());
      entries.sort((a, b) => a[1].timestamp - b[1].timestamp);
      
      for (let i = 0; i < entries.length - this.maxSize; i++) {
        this.cache.delete(entries[i][0]);
      }
    }
  }
  
  // 清空缓存
  clear() {
    this.cache.clear();
  }
}

// 使用示例
const moduleCache = new ModuleCache();

async function loadModuleWithCache(modulePath) {
  return moduleCache.get(modulePath, async () => {
    const response = await fetch(modulePath);
    return response.text();
  });
}

八、模块化最佳实践与工程化

8.1 模块设计原则
javascript 复制代码
// 1. 单一职责原则
// 不好的例子
class UserManager {
  // 混合了用户管理、验证、通知等多个职责
}

// 好的例子
class UserRepository {
  // 只负责数据访问
}

class UserValidator {
  // 只负责验证
}

class UserNotifier {
  // 只负责通知
}

// 2. 依赖注入
class UserService {
  constructor(userRepository, validator, notifier) {
    this.userRepository = userRepository;
    this.validator = validator;
    this.notifier = notifier;
  }
  
  async register(user) {
    if (!this.validator.validate(user)) {
      throw new Error('Invalid user');
    }
    
    await this.userRepository.save(user);
    await this.notifier.sendWelcome(user.email);
  }
}

// 3. 接口抽象
// 定义接口
class IStorage {
  async save(key, value) {}
  async get(key) {}
  async delete(key) {}
}

// 具体实现
class LocalStorage extends IStorage {
  async save(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }
  
  async get(key) {
    return JSON.parse(localStorage.getItem(key));
  }
  
  async delete(key) {
    localStorage.removeItem(key);
  }
}

class APIService {
  constructor(storage) {
    if (!(storage instanceof IStorage)) {
      throw new Error('Invalid storage implementation');
    }
    this.storage = storage;
  }
}
8.2 模块版本管理与升级
javascript 复制代码
class ModuleVersionManager {
  constructor() {
    this.versions = new Map();
    this.deprecations = new Map();
  }
  
  // 注册模块版本
  register(moduleName, version, module) {
    if (!this.versions.has(moduleName)) {
      this.versions.set(moduleName, new Map());
    }
    
    this.versions.get(moduleName).set(version, module);
  }
  
  // 获取模块(支持语义化版本)
  get(moduleName, versionRange = 'latest') {
    const moduleVersions = this.versions.get(moduleName);
    if (!moduleVersions) {
      throw new Error(`Module ${moduleName} not found`);
    }
    
    if (versionRange === 'latest') {
      const latestVersion = Array.from(moduleVersions.keys())
        .sort(this.compareVersions)
        .pop();
      return moduleVersions.get(latestVersion);
    }
    
    // 简化的版本范围解析
    const availableVersions = Array.from(moduleVersions.keys())
      .filter(v => this.satisfiesVersion(v, versionRange))
      .sort(this.compareVersions);
    
    if (availableVersions.length === 0) {
      throw new Error(`No version of ${moduleName} satisfies ${versionRange}`);
    }
    
    return moduleVersions.get(availableVersions.pop());
  }
  
  // 比较版本
  compareVersions(v1, v2) {
    const parts1 = v1.split('.').map(Number);
    const parts2 = v2.split('.').map(Number);
    
    for (let i = 0; i < 3; i++) {
      if (parts1[i] !== parts2[i]) {
        return parts1[i] - parts2[i];
      }
    }
    
    return 0;
  }
  
  // 检查版本是否满足范围
  satisfiesVersion(version, range) {
    // 简化实现,实际应使用semver库
    if (range === '*') return true;
    
    const [op, versionRange] = range.match(/^([>=<~^]*)(\d+\.\d+\.\d+)$/).slice(1);
    const vParts = version.split('.').map(Number);
    const rParts = versionRange.split('.').map(Number);
    
    switch (op) {
      case '^': // 兼容版本
        return vParts[0] === rParts[0] && vParts[1] >= rParts[1];
      case '~': // 近似版本
        return vParts[0] === rParts[0] && 
               vParts[1] === rParts[1] && 
               vParts[2] >= rParts[2];
      case '>=':
        return this.compareVersions(version, versionRange) >= 0;
      case '>':
        return this.compareVersions(version, versionRange) > 0;
      case '<=':
        return this.compareVersions(version, versionRange) <= 0;
      case '<':
        return this.compareVersions(version, versionRange) < 0;
      default:
        return version === versionRange;
    }
  }
  
  // 弃用通知
  deprecate(moduleName, version, message) {
    if (!this.deprecations.has(moduleName)) {
      this.deprecations.set(moduleName, new Map());
    }
    
    this.deprecations.get(moduleName).set(version, {
      message,
      deprecatedAt: new Date()
    });
    
    // 添加控制台警告
    console.warn(`Module ${moduleName}@${version} is deprecated: ${message}`);
  }
}

// 使用示例
const versionManager = new ModuleVersionManager();

// 注册不同版本
versionManager.register('utils', '1.0.0', {
  oldMethod: () => 'old'
});

versionManager.register('utils', '1.1.0', {
  oldMethod: () => 'old',
  newMethod: () => 'new'
});

versionManager.register('utils', '2.0.0', {
  newMethod: () => 'new',
  betterMethod: () => 'better'
});

// 标记弃用
versionManager.deprecate('utils', '1.0.0', '请升级到1.1.0+版本');

// 获取模块
const utilsV1 = versionManager.get('utils', '^1.0.0');
console.log(utilsV1); // 1.1.0版本

const utilsLatest = versionManager.get('utils');
console.log(utilsLatest); // 2.0.0版本

总结

模块化是现代前端工程化的基石,从前端的脚本标签到ES Modules,再到模块联邦等高级模式,模块化技术不断演进。本文从简单模块加载器实现开始,逐步深入AMD、CMD规范,探讨ES Module的Polyfill技术,并补充了构建工具集成、模块联邦、性能优化等工程化实践。

关键要点总结:

  1. 模块加载器核心原理: 依赖管理、缓存、异步加载
  2. 规范演进: 从AMD/CMD到ES Modules的统一
  3. 工程化实践: 代码分割、懒加载、版本管理、依赖注入
  4. 未来趋势: 模块联邦、微前端架构、Web Assembly模块化

模块化不仅仅是技术选择,更是一种设计哲学。良好的模块化设计能够提升代码的可维护性、可测试性和团队协作效率。在实际项目中,应根据团队规模、项目复杂度和技术栈选择合适的模块化方案,并不断优化模块边界和依赖关系。

随着前端技术的不断发展,模块化将继续演进,但核心原则------关注点分离、接口抽象、依赖管理------将始终保持不变。掌握模块化的核心原理和实践,能够帮助开发者构建更健壮、可维护的前端应用。

相关推荐
软件技术NINI2 小时前
如何学习前端
前端·学习
想用offer打牌2 小时前
面试官问Redis主从延迟导致脏数据读怎么解决?
redis·后端·面试
weixin_422555422 小时前
ezuikit-js官网使用示例
前端·javascript·vue·ezuikit-js
鱼鱼块2 小时前
从零搭一个 Vue 小家:用 Vite + 路由轻松入门现代前端开发
vue.js·面试·前端框架
懒猫爱上鱼2 小时前
Android 四大组件与 AMS 交互的完整对比
面试
梓仁沐白2 小时前
CSAPP-Attacklab
前端
华清远见成都中心2 小时前
嵌入式工程师技术面试有哪些注意事项?
面试·职场和发展
沐雪架构师2 小时前
大模型Agent面试精选15题(第三辑)LangChain框架与Agent开发的高频面试题
面试·职场和发展
郑州光合科技余经理2 小时前
海外国际版同城服务系统开发:PHP技术栈
java·大数据·开发语言·前端·人工智能·架构·php