JS模块化开发之EsModule和Common.js区别
作者:wyzqhhhh
1. 语法(Import/Export vs Require/Exports)
ESM 使用 静态的 import 和 export,而 CommonJS 使用 动态的 require 和 module.exports。
ESModule使用 import 和 export 语法来导入和导出模块。
导入:
import { functionName } from './module.js'; 导出:
export const functionName = () => {}; CommonJS 使用 require 和 module.exports 语法来导入和导出模块。
导入:
const { functionName } = require('./module'); 导出:
module.exports.functionName = () => {}; 2. 静态 vs 动态加载
ESM 是静态的,支持 编译时优化,如 Tree Shaking。
CommonJS 是动态的,模块加载是 运行时执行,更灵活,但没有静态优化能力。
ESModule(ESM):
静态分析:
ESM是静态的,意味着在编译时,浏览器或构建工具就能够分析出所有的依赖关系。这样可以进行优化(如 Tree Shaking)来删除未使用的代码。import和export语句必须出现在模块的顶部,不能在条件语句或函数中使用。why?
因为 ES Modules(ESM)的设计是「静态的」,不是「动态的」。import和 export必须在模块的顶层(即模块代码的最外层作用域)被静态分析,这样 JavaScript 引擎 / 打包工具 才能在不运行代码的情况下,确定模块之间的依赖关系,进行优化、打包、Tree-Shaking 等操作。
如果你把 import 放到 if 语句、函数、循环等内部,那么:
引擎或打包工具就 无法在代码运行前确定这个模块依赖了谁,因为是否导入取决于运行时的条件,这就违反了“静态模块”的原则。

CommonJS:
动态加载:
CommonJS是动态的,模块是在运行时加载的,这意味着require()调用可以出现在任何地方(例如,在函数或条件语句中)。这为代码的灵活性提供了更多的选择。require()调用是在执行时解析的,因此不能像import一样进行静态优化。
3. 加载机制
ESM 支持 异步加载,适用于现代的浏览器和服务器端。
CommonJS 采用 同步加载,适用于 Node.js 环境,通常是基于文件系统的加载方式。
ESModule:模块是 异步加载 的,在浏览器中,
ESM可以通过网络请求异步加载模块(通过<script type="module">),而在 Node.js 中,ESM模块也可以通过import异步加载。浏览器:
<script type="module" src="module.js">Node.js:使用
import语句加载模块。
CommonJS:模块是 同步加载 的。
require()是在模块执行时同步加载模块的,这意味着当一个模块被加载时,程序会等待该模块加载完成后继续执行。这种方式适合于服务端的应用,如 Node.js。
4. 支持的环境
ESM 是 现代的标准模块化,已被浏览器和 Node.js 逐渐支持。
CommonJS 主要用于 Node.js 环境。
ESModule(ESM):
在现代浏览器中,
ESM已经得到了广泛支持,可以直接通过<script type="module">标签在 HTML 页面中使用。在 Node.js 中,从
v12版本开始也逐渐支持ESM,但是需要使用.mjs扩展名或在package.json文件中设置"type": "module"。
CommonJS:
主要用于 Node.js 环境中,它在浏览器端原生不被支持,需要通过工具(如 Webpack、Browserify)进行打包。
5. 模块的导出方式
ESM 支持 命名导出 和 默认导出。
CommonJS 使用
module.exports导出模块,但只能导出一个对象。
ESModule:
export 可以导出多个变量、函数、类等。
export const name = 'John';
export function greet() { console.log('Hello'); }default 导出:每个模块可以有一个默认导出,可以通过 export default 来指定。
export default function() { console.log('Hello Default'); } 导入:默认导入可以用任意名称来接收导出的默认值。
import func from './module'; // 导入默认导出
CommonJS:
module.exports 用来导出模块。
module.exports.name = 'John';
module.exports.greet = function() { console.log('Hello'); };只能导出一个对象或函数。
导入:require 导入整个模块。
const myModule = require('./module');
myModule.greet();6. 循环依赖
ESM 和 CommonJS 都能处理循环依赖,但 ESM 由于是静态解析,能更好地避免此类问题,并且避免重复加载模块。
ESModule(ESM):
ESM 支持 静态解析,即使在模块之间存在循环依赖,浏览器和构建工具也能正确处理。在循环依赖的情况下,ESM 会返回 模块的已加载版本,而不会重复加载模块。
CommonJS:
CommonJS 也能处理循环依赖,但它是 在运行时解析 的,可能会返回部分加载的模块内容,而不是完全加载的模块。
7. 支持的功能:动态导入
ESM 支持 动态导入,可以按需加载模块。
CommonJS 只有 同步加载 模块。
ESModule:ESM 支持 动态导入,可以通过
import()函数动态加载模块。这使得开发者能够按需加载代码,增强性能。import('./module').then(module => { // 使用 module });CommonJS:CommonJS 没有原生支持动态导入,它的
require是同步的。
总结对比:
| 特性 | ESModule (ESM) | CommonJS | 
|---|---|---|
| 导入导出语法 | import { x } from 'module' / export | const x = require('module') / module.exports | 
| 模块化方式 | 静态模块化,支持树摇(Tree Shaking) | 动态模块化,按需加载 | 
| 加载方式 | 异步加载,适用于浏览器和 Node.js | 同步加载,主要用于 Node.js | 
| 兼容性 | 支持现代浏览器和 Node.js(通过 .mjs 或 "type": "module") | 主要用于 Node.js | 
| 默认导出 | export default | module.exports = | 
| 支持动态导入 | import() 动态导入 | 不支持动态导入 | 
| 循环依赖 | 静态解析,避免重复加载 | 在运行时处理循环依赖,返回部分加载模块 | 
使用场景:
ESModules 是现代的标准模块化方案,推荐用于前端开发以及支持
ESM的 Node.js 环境。CommonJS 主要用于 Node.js 环境,尤其适合传统的服务器端模块。
当浏览器不支持ES Modules时
解决方法:使用 script 标签的 type="module" 和回退方案
1. 使用<script>标签的nomodule属性
HTML5 引入了 nomodule 属性,这个属性可以用来为不支持模块的浏览器提供备用脚本。
原理:
<script type="module">会在支持ES Modules的浏览器中加载,而nomodule会在不支持ES Modules的浏览器中加载。因此,可以通过这种方式为不支持ES Modules的浏览器提供传统的、非模块化的 JavaScript。
示例代码:
<!-- 模块化脚本,在支持 ES Module 的浏览器中执行 --> <script type="module" src="main.mjs"></script> <!-- 非模块化脚本,仅在不支持模块的浏览器中执行 --> <script nomodule src="main-legacy.js"></script>
type="module":浏览器支持ES Modules时,会加载main.mjs脚本。nomodule:仅当浏览器不支持模块时,会加载main-legacy.js脚本。这样,老旧浏览器会执行传统的非模块化脚本。
2. 使用 JavaScript 编译工具(如 Babel)将代码转译为 ES5
如果要支持不支持 ES Modules 的旧版浏览器(如 IE 11 或一些早期版本的 Edge),还需要使用工具将 JavaScript 代码转译为兼容的版本。
Babel:Babel 是一个非常流行的 JavaScript 编译器,它可以将现代 JavaScript 代码(包括
ES Modules)转译为兼容更广泛浏览器的代码。Webpack 或 Rollup:这些打包工具也可以与 Babel 配合使用,将
ES Modules转换为传统的 CommonJS 或 IIFE 模式,从而确保兼容旧浏览器。
配置 Babel 转译ES Modules:
安装 Babel 和相关插件:
如果你还没有 Babel 环境,可以通过以下命令安装 Babel 和必要的插件:npm install --save-dev @babel/core @babel/preset-env babel-loader
Babel 配置文件(babel.config.json):
在
babel.config.json中,配置 Babel 转译ES Modules为 CommonJS 或其他兼容格式:{ "presets": [ [ "@babel/preset-env", { "modules": "commonjs" } ] ] }"modules": "commonjs":这将ES Modules转换为CommonJS,以确保可以在不支持ES Modules的浏览器中运行。
Webpack 配置(可选):
如果使用 Webpack,你可以配置 Webpack 来确保旧浏览器能够处理编译后的 JavaScript:
// webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: __dirname + '/dist' }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] } };最终生成的代码:
Babel 会将现代 JavaScript(如
import/export)转译为兼容 ES5 的代码,这样即使在旧浏览器中,也能正常加载并执行。
3. 使用 Polyfill
如果你的 JavaScript 中使用了其他新的 JavaScript 特性(例如 Promise、fetch 等),而这些特性在旧浏览器中没有实现,可以使用 polyfill 来提供对这些特性的支持。
Polyfill:Polyfill 是一个 JavaScript 库,用于提供对新特性的支持。例如,
core-js是一个流行的 polyfill 库,可以为不支持的浏览器提供ES6、ES7等特性的实现。
如何使用 polyfill:
安装 core-js:
npm install core-js
在 JavaScript 中引入 polyfill:
import 'core-js/stable'; import 'regenerator-runtime/runtime'; // 如果使用了 async/await
这会为不支持的浏览器提供必要的功能实现。
总结:
如果你需要在不支持 ES Modules 的浏览器中运行代码,可以使用以下几种方法:
nomodule 和 type="module":为支持
ES Modules的浏览器加载模块,为不支持的浏览器加载传统脚本。使用 Babel:通过 Babel 将
ES Modules转换为兼容旧浏览器的代码。Polyfill:使用 polyfill 库来填补旧浏览器缺少的功能,确保现代 JavaScript 特性在旧浏览器中能够正常运行。

到此这篇关于JS模块化开发之EsModule和Common.js区别的文章就介绍到这了,更多相关JS EsModule和Common.js区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

