浏览器的原生 ES 模块(ECMAScript Modules,简称 ESM)是 JavaScript 的官方模块系统,它允许开发者以模块化的方式组织代码,并在浏览器中直接使用。以下是浏览器原生 ES 模块的工作原理:
1. 模块的定义
ES 模块通过 import
和 export
关键字来定义和导入模块:
-
导出模块 :使用
export
关键字将模块中的变量、函数或类暴露给其他模块。javascript// math.js export function add(a, b) { return a + b; }
-
导入模块 :使用
import
关键字从其他模块中引入需要的功能。javascript// main.js import { add } from './math.js'; console.log(add(2, 3)); // 输出 5
2. 模块的加载
当浏览器遇到 <script type="module">
标签时,它会将脚本识别为 ES 模块,并启用模块加载机制:
html
<script type="module" src="main.js"></script>
- 浏览器会解析
main.js
,并发现它依赖math.js
。 - 浏览器会异步加载
math.js
,并在加载完成后执行main.js
。
3. 模块的解析
- 依赖图构建 :浏览器会从入口模块(如
main.js
)开始,递归地解析所有import
语句,构建模块依赖图。 - 按需加载:浏览器只会加载当前需要的模块,而不是一次性加载所有代码。这种按需加载的方式可以提高性能。
4. 模块的执行
- 严格模式:ES 模块默认在严格模式下运行,这意味着代码中的一些不严谨写法(如未声明的变量)会报错。
- 单例模式:每个模块只会被加载和执行一次,即使它被多个模块导入。模块的导出值是共享的。
- 异步加载:模块的加载和执行是异步的,不会阻塞页面的渲染。
5. 模块的缓存
- 浏览器会缓存已加载的模块,避免重复下载和执行。如果模块内容没有变化,浏览器会直接使用缓存。
6. 支持动态导入
除了静态的 import
语句,ES 模块还支持动态导入(import()
),允许在运行时按需加载模块:
javascript
// 动态加载模块
import('./math.js').then(module => {
console.log(module.add(2, 3)); // 输出 5
});
7. 与 CommonJS 的区别
- 语法 :CommonJS 使用
require()
和module.exports
,而 ES 模块使用import
和export
。 - 加载方式:CommonJS 是同步加载,适合服务器端(如 Node.js);ES 模块是异步加载,适合浏览器环境。
- 静态分析 :ES 模块的
import
和export
是静态的,可以在编译时分析依赖关系,而 CommonJS 是动态的。
总结
浏览器的原生 ES 模块通过 import
和 export
实现模块化,支持按需加载、异步执行和缓存机制。它提供了一种现代化的模块化开发方式,适合构建复杂的前端应用。Vite 等工具正是利用了这一特性,极大地提升了开发效率。