ESmodule(ECMAScript Module)是 JavaScript 的官方模块化规范,其在浏览器环境中的工作流程主要分为模块解析和模块执行两个核心步骤。
1. 模块解析(下载与依赖分析)
模块解析是指浏览器下载所有依赖模块并构建依赖关系图的过程,具体流程如下:
- 入口模块触发:从入口模块(如)开始,浏览器首先下载入口模块的代码。
- 静态依赖分析:下载完成后,浏览器仅解析顶层静态依赖(语句),忽略动态依赖()。例如,若入口模块中存在,浏览器会立即识别并下载。
- 递归处理依赖:对每个下载的模块(如)重复上述过程,递归解析其顶层静态依赖,直至所有静态依赖模块均下载完成。注意:若模块已下载(如循环依赖或重复导入),不会重复下载,避免冗余。
关键特性:
- 静态依赖必须写在模块顶层,不可嵌套在条件语句(如)或函数中,否则会触发语法错误。
- 浏览器会自动将分散的顶层静态依赖 “提前” 整合,因此建议将所有静态导入集中写在模块开头,避免代码逻辑夹杂。
2. 模块执行(代码运行与缓存)
所有静态依赖模块下载完成后,进入执行阶段,按以下流程运行代码:
- 入口模块开始执行:从入口模块()开始,按代码顺序执行,遇到语句时跳转到对应模块执行。
- 递归执行依赖模块:例如,导入时,优先执行;若又导入,则继续执行,直至最底层依赖模块执行完毕。
- 导出结果缓存:每个模块执行完毕后,其导出内容(默认导出或具名导出)会被缓存到一张 “模块导出表” 中(键值对形式,如)。后续再次导入该模块时,直接读取缓存结果,不会重复执行模块代码。
- 符号绑定机制:导入的变量与导出模块的变量是符号绑定关系(而非值拷贝),即若导出模块后续修改了导出变量,导入方会同步感知到变化(这与 CommonJS 的 “值拷贝” 不同)。
3. 动态导入的处理
对于动态导入(),其流程与静态导入类似,但具有异步性:
- 动态导入在代码执行到该语句时才触发模块解析(下载),且不会阻塞后续代码执行。
- 若动态导入的模块已被缓存(如之前通过静态导入加载过),则直接使用缓存结果;否则重新执行 “解析→执行→缓存” 流程。
ESmodule 与 Commonjs 的区别
总结
ESmodule通过 “编译时解析依赖 + 运行时执行缓存” 的机制,实现了更高效的模块化管理,尤其适合浏览器环境的静态优化。其与Commonjs的核心差异体现在加载时机、依赖处理和导出绑定方式上,这也是面试高频考点。