CommonJS和ES Modules篇

CommonJS和ES Modules篇

1、认识

CommonJS(CJS)ES Modules(ESM) 是JS的两种模块化系统。在模块导入、导出、加载机制、使用场景等方面存在显著的区别。

🍎模块化发展历程

2009 => CommonJS 诞生,被Node.js作为一个标准开始制定使用,作为默认的模块化系统

2010年 =>CommonJS 模块规范正式发布

2015 => ES6 Modules 标准化,同年也是ECMAScript 6(即 ES6)规范的发布

2020 => Node.js 13+ 正式支持 ES Modules

2023 => 90% 的新项目首选 ES Modules

🍎使用场景以及支持的环境

CommonJS

主要用于 Node.js 环境,虽然在浏览器中能通过工具(如 Webpack、Browserify)进行打包,但原生不支持。

ES Modules

浏览器和 Node.js 都支持 ES Modules,且现代浏览器普遍支持 <script type="module"> 标签。

🍎语法区别

CommonJS

CommonJS 是一种同步加载模块的规范,主要用于服务器端的 JavaScript(比如 Node.js)。

plain 复制代码
// 导入模块
const module = require('xxx');


//导出模块
module.exports = { xxx };

或者使用下面的写法

plain 复制代码
exports.xxxFunction = function() { 
    xxx
};
ES Modules

ES Modules是 JavaScript 官方标准(ECMAScript)的一部分,旨在在浏览器和服务器端(如 Node.js)中提供一致的模块化支持。

plain 复制代码
// 导入模块 方式一 (推荐使用)
import { someFunction } from './module-name';

// 导入模块 方式二
import * as module from './xxx';
plain 复制代码
// 导出模块 方式一(推荐使用)
export const someFunction = () => { };

// 导出模块 方式二
export default function() {  };

2、CommonJS和ES Modules发展及解决问题

2009以前

JavaScript 初期,浏览器环境中的 JavaScript 程序通常是单一的、全局作用域的,所有代码都混合在一起。程序复杂以后,开发者的代码急切的需要一种模块化机制来组织代码、提高可重用性和可维护性

为了应对 Node.js 环境中对模块的需求,Node.js(2009年发布)使用了CommonJS作为默认的模块化系统,Node.js制定标准CommonJS,2010年CommonJS模块正式发布。

CommonJS 模块是同步加载的,使得它适用于服务器端应用。

但CommonJS 模块是在运行时加载的,导致无法进行静态分析,像 Tree Shaking 和 代码分割(Code Splitting) 这样的优化无法在打包时进行

2015年ECMAScript 6(即 ES6)规范发布,内建模块系统ES Modules(ESM),ES6 Modules 彻底标准化。ESM 的静态结构使得 JavaScript 引擎能够在编译时进行优化,Tree Shaking 和 代码分割(Code Splitting)信手拈来,并且**跨平台支持。**2020年Node.js 13+ 全力支持 ES Modules,ESM成为主流。

3、CommonJS和ESM的区别和不同

🍎加载方式的区别

CommonJS同步加载,当执行 require() 时,Node.js 会立即加载并执行模块代码。加载后的模块被缓存,并且返回模块的导出对象。适合于服务器端的同步需求。

CommonJS不能在浏览器中直接使用(直到现代浏览器支持 ES Modules)

ES Modules是异步加载 的,适用于浏览器和服务器端

支持静态分析,编译器和打包工具可以提前分析模块的依赖关系

支持动态导入,使用 import() 函数异步加载模块

涉及到按需加载和优化性能时非常好用

🍎模块的解析和执行时机

CommonJS模块代码立即执行,当 require() 被调用时,模块会立即被加载并执行一次

ESModules模块代码按需加载,模块的导入是静态绑定,导入的值是模块首次执行时的值,且不再变化

🍎默认导出

CommonJS使用 module.exports 进行导出,默认导出的模块对象不需要特别标注

plain 复制代码
// CommonJS 导出
module.exports = function() { /* ... */ };

ES Modules支持 default 导出,直接导出一个默认值

plain 复制代码
// ES Modules 导出
export default function() { /* ... */ };

🍎模块缓存

CommonJS在第一次被 require() 时会被加载并缓存,后续的 require() 调用将直接返回缓存的模块对象

ES Modules也会被缓存,但每个模块导入时,它是只读的,按照静态结构导出,不会改变

🍎在 Node.js的支持

CommonJS

Node.js 原生支持 CommonJS,从 Node.js 的最早版本开始就被广泛使用

ES Modules

在 Node.js 中,从 v12.x 开始引入对 ES Modules 的支持,但需要使用 .mjs 文件扩展名或通过 "type": "module" 配置项启用对 ESM 的支持。

到 Node.js 14 版本,ESM 才逐渐变得更加稳定和广泛使用。

🍎循环依赖处理

CommonJS

CommonJS 处理循环依赖时,返回的是已加载的模块的部分内容,这可能导致循环依赖的一些问题。

ES Modules

ESM 通过 "死锁" 模式来处理循环依赖,确保模块的内容始终是稳定和一致的。

4、两者对比

特性 CommonJS ESModule (ESM)
加载方式 同步加载 异步加载(支持动态导入) 支持懒加载
模块语法 require() module.exports import export
跨平台兼容性 主要适用于 Node.js,浏览器需使用工具支持 浏览器和 Node.js 都支持
导出类型 无法区分默认导出与命名导出 支持默认导出 (export default
模块缓存 会缓存已加载模块 缓存且模块是只读的
循环依赖处理 可能会返回部分模块 更好地处理循环依赖
适用 服务器端的同步加载和模块化 现代的前端开发和浏览器环境
优点 兼容性良好、同步加载、简单易用 支持异步加载、静态分析和更清晰的模块结构
静态分析和优化 无法进行静态分析,不能进行优化 支持静态分析 支持Tree Shaking (去除未使用的代码) 代码分割(Code Splitting)
性能 较为适合服务器端,浏览器中性能较差 支持懒加载和优化,适用于浏览器和 Node.js
生态系统 已有庞大的生态系统,几乎所有 Node.js 包都使用 生态系统逐步成长,尤其是在现代 JavaScript 项目中
互操作性 与其他模块系统兼容良好 与 CommonJS 存在兼容性问题
支持的 Node.js 版本 所有 Node.js 版本均支持 Node.js 从 v12 开始全面支持 ESM
错误处理与模块执行顺序 顺序执行,无异步问题 模块执行有异步加载的潜在影响
学习曲线 简单,易于上手 对于老旧项目的迁移可能需要更多工作
**顶层 ****await**支持 不支持 支持
相关推荐
m0_738120723 小时前
CTFshow系列——命令执行web53-56
前端·安全·web安全·网络安全·ctfshow
PAK向日葵4 小时前
【算法导论】XM 0823 笔试题解
算法·面试
Liu.7745 小时前
uniappx鸿蒙适配
前端
山有木兮木有枝_6 小时前
从代码到创作:探索AI图片生成的神奇世界
前端·coze
言兴6 小时前
秋招面试---性能优化(良子大胃袋)
前端·javascript·面试
WebInfra7 小时前
Rspack 1.5 发布:十大新特性速览
前端·javascript·github
雾恋8 小时前
我用 trae 写了一个菜谱小程序(灶搭子)
前端·javascript·uni-app
烛阴8 小时前
TypeScript 中的 `&` 运算符:从入门、踩坑到最佳实践
前端·javascript·typescript
Java 码农9 小时前
nodejs koa留言板案例开发
前端·javascript·npm·node.js
ZhuAiQuan10 小时前
[electron]开发环境驱动识别失败
前端·javascript·electron