模块化体系

1.7 模块化体系

模块化是现代前端工程化的核心基石,通过将代码拆分为独立、可复用的模块,实现作用域隔离、依赖管理、按需加载与代码复用。

ES6 推出的 ES Modules(ESM)是 JavaScript 官方标准化的模块化方案,统一了早期 CommonJS、AMD 等多种社区规范,是当前前端开发的标准模块化方式,也是构建工具、框架生态的底层基础。


1.7.1 ES Modules:import /export 标准语法

ES Modules 基于 import 和 export 关键字实现模块的导入与导出,模块文件自动拥有独立作用域,只有显式导出的成员才能被外部访问。

一、export 导出语法

导出分为命名导出默认导出两种形式,支持单个导出、批量导出、重命名导出与聚合导出。

  1. 命名导出
  2. 一个模块可以有多个命名导出,导入时需与导出名严格对应。
javascript 复制代码
// 单个声明时直接导出
export const baseURL = 'https://api.example.com';
export function formatDate(date) {
  return date.toISOString().slice(0, 10);
}
export class User {
  constructor(name) {
    this.name = name;
  }
}

// 声明后统一批量导出
const version = '1.0.0';
function log(msg) {
  console.log(msg);
}
export { version, log };
  1. 重命名导出
  2. 通过 as 关键字修改导出的对外名称。
javascript 复制代码
const innerFn = () => {};
export { innerFn as publicFn };
  1. 默认导出
  2. 一个模块只能有一个默认导出,对应一个默认值,适合导出单个核心成员(如单个类、单个函数)。
javascript 复制代码
// 导出函数
export default function add(a, b) {
  return a + b;
}

// 导出对象
export default {
  name: 'utils',
  version: '1.0.0'
};
  1. 聚合导出(中转导出)
  2. 在入口模块中统一转发其他模块的导出,简化外部引用路径。
javascript 复制代码
// 转发所有命名导出
export * from './utils.js';
// 转发并重命名
export { default as DateUtil } from './date.js';
二、import 导入语法

对应导出语法,导入分为命名导入、默认导入、整体导入与重命名导入。

  1. 命名导入
  2. 使用花括号包裹,导入名必须与导出名完全一致。
javascript 复制代码
import { baseURL, formatDate } from './config.js';
console.log(baseURL);
  1. 重命名导入
  2. 通过 as 修改导入后的变量名,解决命名冲突问题。
javascript 复制代码
import { formatDate as format } from './config.js';
  1. 默认导入
  2. 无需花括号,可自定义导入名称。
javascript 复制代码
import add from './add.js';
console.log(add(1, 2));
  1. 整体命名空间导入
  2. 通过 * as 将模块所有命名导出挂载到一个对象上。
javascript 复制代码
import * as utils from './utils.js';
console.log(utils.version);
utils.log('test');
三、基础语法约束
  • import / export 只能出现在模块顶层作用域,不能嵌套在函数、条件语句等块级作用域中
  • 模块路径支持相对路径与绝对路径,省略后缀时需构建工具支持
  • 导入的变量是只读引用,不可在导入侧直接修改导出值

1.7.2 动态 import () 按需加载

静态 import 语法必须在模块顶层执行,无法实现条件加载与按需加载。ES2020 引入的动态 import() 以函数形式调用,返回一个 Promise 对象,可在任意时机动态加载模块,是性能优化的核心手段。

一、基础语法
javascript 复制代码
import(modulePath)
  .then(module => {
    // module 为模块导出对象
    module.default();
  })
  .catch(err => {
    console.error('模块加载失败:', err);
  });

配合 async/await 使用更简洁:

javascript 复制代码
async function loadModule() {
  try {
    const utils = await import('./utils.js');
    utils.doSomething();
  } catch (err) {
    console.error('加载失败', err);
  }
}
二、典型应用场景
  1. 条件加载:根据运行环境、特性支持度按需加载模块
javascript 复制代码
if (!window.IntersectionObserver) {
  await import('./intersection-polyfill.js');
}
  1. 交互触发加载:用户触发特定操作时再加载对应功能模块,降低首屏体积
javascript 复制代码
// 点击按钮后再加载弹窗模块
modalBtn.addEventListener('click', async () => {
  const Modal = (await import('./Modal.js')).default;
  new Modal().show();
});
  1. 路由懒加载:单页应用中按路由拆分代码,进入对应页面时才加载页面资源
  2. 大依赖延迟加载:体积较大的第三方库(如图表、富文本编辑器)在使用前再加载

1.7.3 模块作用域与依赖机制

ES Modules 拥有独立的作用域与明确的依赖执行规则,从语法层面解决了全局变量污染、依赖顺序混乱等问题。

一、模块作用域

每个 ES Module 都拥有独立的私有作用域:

  • 模块内声明的变量、函数、类默认仅在模块内部可见,不会泄露到全局作用域
  • 只有通过 export 显式导出的成员,才能被外部模块访问
  • 不同模块之间的同名变量不会互相冲突

对比传统脚本文件:多个普通 script 标签共享全局作用域,容易出现变量覆盖、命名冲突;模块文件天然隔离,无需手动封装闭包。

二、单例与引用机制
  • 单例执行:同一个模块被多次导入时,仅会在第一次导入时执行一次模块代码,后续导入直接复用导出结果,不会重复执行
  • 值的引用:导入的是导出值的引用,而非值的拷贝。若模块内部修改了导出的变量,所有导入该模块的地方都会同步更新
javascript 复制代码
// counter.js
export let count = 0;
export function increment() {
  count++;
}

// a.js
import { count, increment } from './counter.js';
increment();
console.log(count); // 1

// b.js
import { count } from './counter.js';
console.log(count); // 1,与 a.js 共享同一份引用
三、依赖执行规则

模块按照依赖树深度优先的顺序执行:

  1. 先执行当前模块的所有依赖模块,再执行当前模块自身代码
  2. 循环依赖场景下,已执行的部分会正常导出,未执行的部分为 undefined,需避免复杂循环依赖
四、默认严格模式

所有 ES Module 自动运行在严格模式下,无需手动声明 'use strict',因此模块内默认遵循严格模式的所有约束(如禁止未声明变量赋值、禁止 with 语句等)。

相关推荐
benbenAItalk1 分钟前
数字人口播视频的批量生产实践:素材规范、任务编排与质量验收
java·前端·音视频
2601_9628857221 分钟前
如何用 Python 扫描 A 股跳空缺口并统计缺口回补概率?
java·前端·python
西瓜太郎12341 小时前
Claude Code、Codex CLI、Gemini CLI 能否共用一枚 Key?先看协议选择矩阵
前端·api 网关·claude code·gemini cli·codex cli
陈随易3 小时前
在Finch用了62亿词元,我认为这是新一代Agent工具之神
前端·人工智能·后端
水域安全老周3 小时前
水趣钓鱼救生衣专利拆解:两级锁紧如何解决落水人衣分离
java·前端·网络
计算机魔术师4 小时前
Anthropic CEO突然喊踩刹车,OpenAI罕见力挺:AI这辆车不能只踩油门了
前端
wing984 小时前
从codex转战workbuddy使用一周的感受
前端·人工智能·后端
EatFan4 小时前
Java接入支付宝 JSAPI 支付保姆教程(二):流程讲解与前后端代码讲解
前端·spring boot·后端·微信小程序·小程序·uni-app
梦想平凡5 小时前
百游棋牌源代码开发搭建教程(五):房间创建、座位分配与请求幂等实现
前端·javascript·数据库·源代码管理
IT_陈寒5 小时前
Redis的Set操作居然能把我的服务整挂了?
前端·人工智能·后端