尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

前端模块化: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 与 UMD6.1 AMD6.2 UMD七、总结参考资源前言模块化是 JavaScript 绕不开的话题。从require到import从值的拷贝到值的引用这篇文章把模块化最核心的内容梳理了一遍。先看两段代码// 代码 Aconstfsrequire(fs)module.exports{name:app}// 代码 Bimportfsfromfsexportconstnameapp这两段代码都在做同一件事——导入和导出模块但写法完全不同。工作中你可能两种都见过甚至在一个项目里同时出现过。它们有什么区别为什么会有两种写法什么时候用哪个如果你正在被这些问题困扰这篇文章或许能给你一些答案。一、为什么前端需要模块化在模块化出现之前JavaScript 代码是这样引入的scriptsrcjquery.js/scriptscriptsrcutils.js/scriptscriptsrcmain.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 的事实标准。语法示例// 导入constfsrequire(fs)constpathrequire(path)// 导出方式一module.exportsmodule.exports{name:John,sayHello(){console.log(Hello)}}// 导出方式二exportsexports.age18核心特点特点说明加载方式同步加载阻塞式执行时机代码运行时执行缓存机制模块加载后会被缓存多次 require() 返回同一对象值传递导出的是值的拷贝浅拷贝动态加载✅ 支持条件 require()Tree Shaking❌ 不支持四、ES Module诞生背景2015 年ECMA International 在 ES2015ES6规范中正式将模块系统纳入 JavaScript 语言标准。这是 JavaScript 语言层面的官方模块方案也是未来的统一方向。语法示例// 导入方式importfsfromfs// 默认导入import{readFile}fromfs// 命名导入import*asfsfromfs// 全部导入// 导出方式exportconstnameJohn// 命名导出exportfunctionsayHello(){}// 命名导出exportdefault{name:John}// 默认导出核心特点特点说明加载方式浏览器异步 / Node.js 同步执行时机代码解析时确定依赖静态分析缓存机制模块只执行一次但 import 是引用值传递导出的是值的引用动态绑定动态加载✅ 支持 import()Tree Shaking✅ 支持得益于静态结构 快速分辨看到require()、module.exports、exports.xxx就是 CommonJS看到import、export、export default就是 ES Module。五、CommonJS vs ES Module四个核心区别对比维度CommonJSES Module加载方式同步阻塞异步非阻塞值传递值的拷贝值的引用动态加载✅ 支持条件 require()✅ 支持 import()Tree Shaking❌ 不支持✅ 支持5.1 同步 vs 异步// CommonJS同步加载会阻塞后续代码constdatarequire(./data.json)console.log(等上面加载完才执行)// ES Module异步加载不阻塞渲染importdatafrom./data.jsonconsole.log(可能比数据先执行)5.2 值的拷贝 vs 值的引用最容易踩坑// CommonJS值的拷贝 // counter.jsletcount0module.exports{count,increment:()count}// main.jsconstcounterrequire(./counter)counter.increment()console.log(counter.count)// 0 ❌ 不变值的拷贝// ES Module值的引用 // counter.jsexportletcount0exportconstincrement()count// main.jsimport{count,increment}from./counterincrement()console.log(count)// 1 ✅ 变了值的引用5.3 动态加载能力// CommonJS支持条件 requireif(envprod){constloggerrequire(./logger-prod)}else{constloggerrequire(./logger-dev)}// ES Module顶层 import 必须静态importloggerfrom./logger// 只能写在顶层// ES Module动态加载用 import()返回 Promiseif(envprod){constloggerawaitimport(./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 与 UMD6.1 AMD诞生背景CommonJS 的 require() 是同步加载的在 Node.js 服务器环境读取本地文件没有问题。但在浏览器里模块文件需要通过网络请求获取同步加载会卡住页面渲染。AMDAsynchronous 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.jsCommonJS可能是浏览器AMD也可能就是直接加载一个script标签全局变量。UMDUniversal Module Definition就是为了解决这个问题。核心思路UMD没有创造新的模块规范它就是一个环境检测器上场后先环顾四周判断当前是CommonJS环境、AMD环境、还是啥也没有的全局环境然后自动选择最合适的加载方式。语法示例简化版// UMD 的核心思想环境判断 适配if(typeofdefinefunctiondefine.amd){// AMD 环境define([jquery],factory)}elseif(typeofmoduleobjectmodule.exports){// CommonJS 环境module.exportsfactory(require(jquery))}else{// 浏览器全局环境window.MyLibfactory(window.jQuery)}核心特点特点说明运行环境浏览器 Node.js加载方式同步兼容性自动判断环境兼容 AMD、CommonJS、全局变量使用场景需要跨环境运行的第三方库现状新项目不会直接写UMD代码了。但你会在node_modules里看到很多第三方库的打包文件比如lodash.min.js它们为了兼容各种环境最终被编译成了UMD格式——你正常用就行构建工具会自动处理。小结AMD 和 UMD 在日常开发中基本用不到但了解它们能帮你理解两件事为什么浏览器需要异步加载模块AMD以及一个库为什么能在不同环境下都能运行UMD。七、总结新项目用ES Module维护老项目用CommonJS遇到AMD/UMD知道它们是干什么的就行。核心要点CommonJSNode.js 的传统方案require()同步加载导出的是值的拷贝ES Module官方标准import异步加载导出的是值的引用支持Tree ShakingAMD为了解决浏览器异步加载而生现在新项目不推荐UMD万能适配器让一个库能在各种环境下运行参考资源MDN: JavaScript 模块 — ES Module 官方指南Node.js CommonJS 模块文档 — CommonJS 权威实现英文阮一峰: CommonJS 规范 — CommonJS 中文解读AMD 规范仓库 (核心文档) — AMD 规范原文阮一峰: ES6 Module 语法 — ES Module 中文教程
返回列表