模块

ES6 模块系统为 JavaScript 提供了强大的代码组织和重用能力。

基本语法

导出模块

// math.js
export const PI = 3.14159

export function add(a, b) {
  return a + b
}

export function multiply(a, b) {
  return a * b
}

export class Calculator {
  static add(a, b) {
    return a + b
  }
}

// 默认导出
export default function subtract(a, b) {
  return a - b
}

// 重命名导出
export { add as sum, multiply as product }

导入模块

// main.js
import { add, multiply, PI } from './math.js'
import subtract from './math.js'
import { sum, product } from './math.js'
import * as MathModule from './math.js'

console.log(add(2, 3))        // 5
console.log(multiply(4, 5))   // 20
console.log(PI)               // 3.14159
console.log(subtract(10, 3))  // 7
console.log(sum(1, 2))        // 3
console.log(MathModule.add(2, 3)) // 5

动态导入

// 条件导入
if (condition) {
  import('./module.js').then(module => {
    // 使用模块
  })
}

// 异步函数中的导入
async function loadModule() {
  const module = await import('./module.js')
  return module
}

// 按需加载
const button = document.getElementById('loadButton')
button.addEventListener('click', async () => {
  const { heavyFunction } = await import('./heavy-module.js')
  heavyFunction()
})

模块模式

传统模块模式

// 立即执行函数表达式 (IIFE)
const Module = (function() {
  let privateVariable = 'secret'

  function privateFunction() {
    console.log('This is private')
  }

  return {
    publicVariable: 'public',
    publicFunction() {
      privateFunction()
      return privateVariable
    }
  }
})()

console.log(Module.publicVariable)  // 'public'
Module.publicFunction()             // 'This is private' and returns 'secret'

现代模块模式

// utils.js
let privateCounter = 0

export function increment() {
  privateCounter++
  return privateCounter
}

export function getCounter() {
  return privateCounter
}

// main.js
import { increment, getCounter } from './utils.js'

console.log(getCounter())  // 0
increment()
increment()
console.log(getCounter())  // 2

模块加载器

基本加载器

class ModuleLoader {
  constructor() {
    this.modules = new Map()
    this.loading = new Map()
  }

  async loadModule(name) {
    if (this.modules.has(name)) {
      return this.modules.get(name)
    }

    if (this.loading.has(name)) {
      return this.loading.get(name)
    }

    const loadPromise = this._loadModule(name)
    this.loading.set(name, loadPromise)

    try {
      const module = await loadPromise
      this.modules.set(name, module)
      this.loading.delete(name)
      return module
    } catch (error) {
      this.loading.delete(name)
      throw error
    }
  }

  async _loadModule(name) {
    // 模拟异步加载
    const response = await fetch(`/modules/${name}.js`)
    const code = await response.text()

    // 创建模块作用域
    const module = { exports: {} }
    const require = (dep) => this.loadModule(dep)

    // 执行代码
    const wrapper = new Function('module', 'exports', 'require', code)
    wrapper(module, module.exports, require)

    return module.exports
  }
}

// 使用加载器
const loader = new ModuleLoader()

async function main() {
  const math = await loader.loadModule('math')
  console.log(math.add(2, 3))
}

main()

模块打包

基本打包器

class ModuleBundler {
  constructor() {
    this.modules = new Map()
    this.bundle = ''
  }

  addModule(name, code, dependencies = []) {
    this.modules.set(name, { code, dependencies })
  }

  generateBundle(entryModule) {
    const processed = new Set()
    const ordered = []

    function processModule(name) {
      if (processed.has(name)) return
      processed.add(name)

      const module = this.modules.get(name)
      if (!module) throw new Error(`Module ${name} not found`)

      // 处理依赖
      for (let dep of module.dependencies) {
        processModule.call(this, dep)
      }

      ordered.push(name)
    }

    processModule.call(this, entryModule)

    // 生成打包代码
    this.bundle = `
(function(modules) {
  const cache = {}

  function require(name) {
    if (cache[name]) return cache[name]

    const module = { exports: {} }
    modules[name](module, module.exports, require)
    cache[name] = module.exports
    return module.exports
  }

  require('${entryModule}')
})({
${ordered.map(name => {
  const module = this.modules.get(name)
  return `  '${name}': function(module, exports, require) {
${module.code}
  }`
}).join(',\n')}
})
`
    return this.bundle
  }
}

// 使用打包器
const bundler = new ModuleBundler()

bundler.addModule('math', `
  exports.add = function(a, b) { return a + b }
  exports.multiply = function(a, b) { return a * b }
`)

bundler.addModule('app', `
  const math = require('math')
  console.log('2 + 3 =', math.add(2, 3))
  console.log('4 * 5 =', math.multiply(4, 5))
`, ['math'])

const bundle = bundler.generateBundle('app')
console.log(bundle)

总结

ES6 模块系统为 JavaScript 带来了现代化的代码组织方式:

  1. 基本语法:import 和 export 语句
  2. 动态导入:按需加载模块
  3. 模块模式:封装私有状态
  4. 加载器:自定义模块加载逻辑
  5. 打包器:将多个模块打包成单个文件

模块化开发提高了代码的可维护性和重用性。