当前位置: 首页 > news >正文

前端模块化:CommonJS 和 ES Module 到底有什么区别?

前端模块化:CommonJS 和 ES Module 到底有什么区别?

    • 前言
    • 一、为什么前端需要模块化?
    • 二、四种模块规范速览
    • 三、CommonJS
    • 四、ES Module
    • 五、CommonJS vs ES Module:四个核心区别
      • 5.1 同步 vs 异步
      • 5.2 值的拷贝 vs 值的引用(最容易踩坑)
      • 5.3 动态加载能力
      • 5.4 Tree Shaking 支持
    • 六、AMD 与 UMD
      • 6.1 AMD
      • 6.2 UMD
    • 七、总结
    • 参考资源

前言

模块化是 JavaScript 绕不开的话题。从requireimport,从值的拷贝到值的引用,这篇文章把模块化最核心的内容梳理了一遍。

先看两段代码:

// 代码 Aconstfs=require('fs')module.exports={name:'app'}// 代码 Bimportfsfrom'fs'exportconstname='app'

这两段代码都在做同一件事——导入和导出模块,但写法完全不同。

工作中你可能两种都见过,甚至在一个项目里同时出现过。它们有什么区别?为什么会有两种写法?什么时候用哪个?

如果你正在被这些问题困扰,这篇文章或许能给你一些答案。

一、为什么前端需要模块化?

在模块化出现之前,JavaScript 代码是这样引入的:

<scriptsrc="jquery.js"></script><scriptsrc="utils.js"></script><scriptsrc="main.js"></script>

这种方式的 3 个致命问题:

问题说明真实场景
🌍全局污染所有变量都挂在window两个库都定义了version变量,后者覆盖前者
🔗依赖混乱必须手动保证加载顺序main.js必须在jquery.js之后加载,否则报错
📦无法复用代码难以在不同项目中共享复制粘贴是常态,维护成本极高

模块化就是为解决这些问题的:每个文件有独立作用域、明确声明依赖、可以导出和导入。

二、四种模块规范速览

在开始之前,先对四种模块规范有个整体印象:

规范全称主要运行环境加载方式
CommonJSCommonJSNode.js同步
ES ModuleECMAScript Module浏览器 / Node.js异步
AMDAsynchronous Module Definition浏览器(RequireJS)异步
UMDUniversal Module Definition浏览器 / Node.js同步

日常开发中,CommonJS 和 ES Module 占据了绝大多数场景,AMD 和 UMD 只需要有个基本概念就行。

三、CommonJS

诞生背景:2009 年,Mozilla 工程师 Kevin Dangoor 发起了一个叫 ServerJS 的项目(后更名为 CommonJS),希望让 JavaScript 也能在服务器端开发。Node.js 选择了 CommonJS 作为其模块系统,从此require()module.exports成为了服务端 JavaScript 的事实标准。

语法示例:

// 导入constfs=require('fs')constpath=require('path')// 导出方式一:module.exportsmodule.exports={name:'John',sayHello(){console.log('Hello')}}// 导出方式二:exportsexports.age=18

核心特点:

特点说明
加载方式同步加载(阻塞式)
执行时机代码运行时执行
缓存机制模块加载后会被缓存,多次 require() 返回同一对象
值传递导出的是值的拷贝(浅拷贝)
动态加载✅ 支持条件 require()
Tree Shaking❌ 不支持

四、ES Module

诞生背景:2015 年,ECMA International 在 ES2015(ES6)规范中正式将模块系统纳入 JavaScript 语言标准。这是 JavaScript 语言层面的官方模块方案,也是未来的统一方向。

语法示例:

// 导入方式importfsfrom'fs'// 默认导入import{readFile}from'fs'// 命名导入import*asfsfrom'fs'// 全部导入// 导出方式exportconstname='John'// 命名导出exportfunctionsayHello(){}// 命名导出exportdefault{name:'John'}// 默认导出

核心特点:

特点说明
加载方式浏览器异步 / Node.js 同步
执行时机代码解析时确定依赖(静态分析)
缓存机制模块只执行一次,但 import 是引用
值传递导出的是值的引用(动态绑定)
动态加载✅ 支持 import()
Tree Shaking✅ 支持(得益于静态结构)

💡 快速分辨:看到require()module.exportsexports.xxx就是 CommonJS;看到importexportexport default就是 ES Module。

五、CommonJS vs ES Module:四个核心区别

对比维度CommonJSES Module
加载方式同步(阻塞)异步(非阻塞)
值传递值的拷贝值的引用
动态加载✅ 支持条件 require()✅ 支持 import()
Tree Shaking❌ 不支持✅ 支持

5.1 同步 vs 异步

// CommonJS:同步加载,会阻塞后续代码constdata=require('./data.json')console.log('等上面加载完才执行')// ES Module:异步加载,不阻塞渲染importdatafrom'./data.json'console.log('可能比数据先执行')

5.2 值的拷贝 vs 值的引用(最容易踩坑)

// ========== CommonJS:值的拷贝 ==========// counter.jsletcount=0module.exports={count,increment:()=>count++}// main.jsconstcounter=require('./counter')counter.increment()console.log(counter.count)// 0 ❌ 不变(值的拷贝)
// ========== ES Module:值的引用 ==========// counter.jsexportletcount=0exportconstincrement=()=>count++// main.jsimport{count,increment}from'./counter'increment()console.log(count)// 1 ✅ 变了(值的引用)

5.3 动态加载能力

// CommonJS:支持条件 requireif(env==='prod'){constlogger=require('./logger-prod')}else{constlogger=require('./logger-dev')}// ES Module:顶层 import 必须静态importloggerfrom'./logger'// 只能写在顶层// ES Module:动态加载用 import()(返回 Promise)if(env==='prod'){constlogger=awaitimport('./logger-prod')}

5.4 Tree Shaking 支持

// utils.jsexportfunctionused(){console.log('used')}exportfunctionunused(){console.log('unused')}// app.jsimport{used}from'./utils'// ES Module 打包后:unused() 被移除(Tree Shaking)// CommonJS:无法做到 Tree Shaking,因为无法静态分析哪些函数被使用了

六、AMD 与 UMD

6.1 AMD

诞生背景:CommonJS 的 require() 是同步加载的,在 Node.js 服务器环境读取本地文件没有问题。但在浏览器里,模块文件需要通过网络请求获取,同步加载会卡住页面渲染。AMD(Asynchronous Module Definition)专门为浏览器设计,支持异步加载。

核心思路:先把所有依赖并行下载,全部下载完成后,再执行回调函数。

语法示例:

// 定义模块define(['jquery','lodash'],function($,_){return{doSomething:function(){// 使用 $ 和 _}}})// 使用模块require(['math'],function(math){console.log(math.add(2,3))})

核心特点:

特点说明
加载方式异步加载(不阻塞渲染)
依赖处理先并行下载所有依赖,再执行回调
代表实现RequireJS
使用场景浏览器端

现状:新项目几乎不用了。只有在维护 5-10 年前用 RequireJS 搭建的老项目时才会遇到。

6.2 UMD

诞生背景:一个库的作者希望自己写的代码不管在什么环境下都能运行——可能是 Node.js(CommonJS),可能是浏览器(AMD),也可能就是直接加载一个<script>标签(全局变量)。UMD(Universal Module Definition)就是为了解决这个问题。

核心思路:UMD没有创造新的模块规范,它就是一个"环境检测器":上场后先环顾四周,判断当前是CommonJS环境、AMD环境、还是啥也没有的全局环境,然后自动选择最合适的加载方式。

语法示例(简化版):

// UMD 的核心思想:环境判断 + 适配if(typeofdefine==='function'&&define.amd){// AMD 环境define(['jquery'],factory)}elseif(typeofmodule==='object'&&module.exports){// CommonJS 环境module.exports=factory(require('jquery'))}else{// 浏览器全局环境window.MyLib=factory(window.jQuery)}

核心特点:

特点说明
运行环境浏览器 + Node.js
加载方式同步
兼容性自动判断环境,兼容 AMD、CommonJS、全局变量
使用场景需要跨环境运行的第三方库

现状:新项目不会直接写UMD代码了。但你会在node_modules里看到很多第三方库的打包文件(比如lodash.min.js),它们为了兼容各种环境,最终被编译成了UMD格式——你正常用就行,构建工具会自动处理。

小结:AMD 和 UMD 在日常开发中基本用不到,但了解它们能帮你理解两件事:为什么浏览器需要异步加载模块(AMD),以及一个库为什么能在不同环境下都能运行(UMD)。

七、总结

新项目用ES Module,维护老项目用CommonJS;遇到AMD/UMD知道它们是干什么的就行。

核心要点

  • CommonJS:Node.js 的传统方案,require()同步加载,导出的是值的拷贝

  • ES Module:官方标准,import异步加载,导出的是值的引用,支持Tree Shaking

  • AMD:为了解决浏览器异步加载而生,现在新项目不推荐

  • UMD:万能适配器,让一个库能在各种环境下运行

参考资源

  • MDN: JavaScript 模块 — ES Module 官方指南
  • Node.js CommonJS 模块文档 — CommonJS 权威实现(英文)
  • 阮一峰: CommonJS 规范 — CommonJS 中文解读
  • AMD 规范仓库 (核心文档) — AMD 规范原文
  • 阮一峰: ES6 Module 语法 — ES Module 中文教程
http://www.cnnetsun.cn/news/4280234.html

相关文章:

  • 为什么说提示词是决定视频质量的天花板
  • Ollama本地部署实战:从安装到API调用与Agent集成
  • 第四范式笔试题复盘:如何把业务问题翻译成机器学习建模方案
  • 零基础Python学习路线:从网络爬虫到数据分析
  • UniDAC 10.3.0源码版在Delphi 12.3中的安装与跨数据库实践
  • ROS2与FAST-LIO2实战:从零搭建高性能激光SLAM系统
  • STM32G0搭配GFX01M1扩展板小屏GUI开发实战指南
  • 快速电流环FCL设计:伺服驱动性能的基石与调试指南
  • UMA for Agents:统一记忆与多Agent编排实战指南
  • OPPO数据开发笔试复盘:SQL与大数据组件考点全解析
  • 外贸独立站建站服务:市场需求、解决方案与市场印证
  • 华为Atlas 300I Duo AI推理卡部署测试全记录:驱动、CANN与批量推理
  • 量化回测:backtrader
  • Littelfuse发布TMR磁性角度传感器:高精度角度检测原理与应用解析
  • 英伟达净利润暴增161%背后:AI算力与GPU基础设施的连锁效应
  • 嵌入式软件知识点自存
  • 数学建模竞赛实战指南:从模型构建到算法求解的完整流程解析
  • AI蛋白质结构“缩小射线”:原理、部署与批量处理指南
  • 432道MySQL面试题 61 - 80 题
  • Python长教程怎么学?把648集当知识地图而非追剧清单
  • Java内推笔试复盘:从冒泡排序到JVM基础考点解析
  • Keil uVision2 C51版详解:从安装配置到工程实战与报错排查
  • 发布订阅模式实战指南:从事件总线原理到消息队列选型与避坑
  • 大厂校招上岸指南:技术干货与面试实战全拆解
  • 从“策略为王”源码看MFC股票行情3秒刷新机制
  • 300集Python零基础教程怎么用?从爬虫到数据分析的学习路径拆解
  • App信息管理系统:从核心功能到技术实现的完整指南
  • HoRain云--Node.js 全局对象
  • LSM303AGR电子罗盘开发:磁校准与倾斜补偿实战
  • Agentic Coding实践:夜间编码智能体(Nightshift)的工程化落地