什么是前端模组化?
2022年2月9日
面试中,关于打包工具的相关知识是非常常见的考题,无可避免的可能会被问到,为什么需要使用到打包工具、或前端工程化的实现,而要回答这些问题前,需要先了解的就是前端模组化。所以在本篇笔记中,会让大家了解前端模组化的演进和功用。
什么是模组化?
开发人员将代码或系统分割为不同模组的过程,就称为模组化。每个模组就代表一个完整的小程序或小功能,所有模组组装起来成为一个整体,进而完成整个系统的功能要求。
Node.js 几乎一开始就支持模组化,相比之下 Web 开发,模组化应用进展慢了许多,但到今日也出现了许多支持前端模组化的工具。
为什么需要前端模组化?
我们先来想像如果今天不用打包工具、模组化语法,要如何在浏览器上执行 JavaScript?
- 方法一:每个功能就是一个 JavaScript script 档案并载入。但此方法会让代码难以扩展,载入太多 script 也会导致网路瓶颈,并且也有载入先后顺序的相依性问题。
- 方法二:使用一个统一的大
.js
档案,将所有功能都包含在其中,但这会导致维护、易读性方面的问题。
模组化工具出现之前,会透过一些方案来解决上述的做法问题,最常见的就是透过立即执行函式(IIFE),它其实是利用函式的闭包特性来实践数据私有化和共享方法,如下范例
const muduleB = (function () {
return {
number: 200,
};
})();
const moduleA = (function (otherModule) {
let number = 100;
function getNumber() {
console.log(number + otherModule.number);
}
return { getNumber };
})(muduleB);
我们可以透过moduleA
拿到getNumber
方法,并且实践number
变数的私有化,防止外部调用,此外moduleA
也可以引入其他module
,通过这种方式,我们就可以在 moduleA 中使用其他模组,进而解决很多问题。
moduleA.getNumber(); // 300
moduleA.number; // undefined
IIFE 的做法也成为现在模组化的概念来源。但随着前端对模组需求越来越大,逐渐出现了一些模组化解决方案、并演变成了通用的规范,从 CommonJS 到现在的 ES Module(ESM)。下个段落会介绍各个通用的模组化规范
延伸阅读:JavaScript 立即调用函式IIFE (Immediately Invoked Function Expression) 是什么?优缺点是什么?
JS 模组化规范
CommonJS
当 Node.js
发布时,它带来了新的挑战。因为 JavaScript 不是在浏览器中运行,因此没有可以添加到其中的 html 文件和脚本标签,那么 Node
应用程序应该如何加载不同的代码呢? - 透过 CommonJS
。 CommonJS
规范在 2009 年被社群开发出来,引入了require
和export
宣告模组的语法,一个文件就是一个模组,是以同步的方式载入模组,因此适合服务器端的开发。
AMD(Async Module Definition)
AMD
在中文是非同步模组定义的意思。它起源于 Dojo 工具箱 (一套 JavaScript Web 应用程式库)。同时,AMD
一开始就是为浏览器而设计的,目前为止,最受欢迎的AMD
实作是{% outerLink href="https://requirejs.org/" text="RequireJS" /% } (一个 JavaScript 模组加载器)。 AMD
可以自动决定相依关系,模组是以非同步方式载入,避免阻塞的问题,并且可以把多个模组定义在同一个档案中。
CMD(Common Module Definition)
CMD
的出现较晚一些, Sea.js
在 2012 年开始推广,它汲取了 CommonJS
和 AMD
规范的优点,也是专门用于浏览器的异步模块加载。在规范中,一个文件就是一个模组。
UMD(Universal Module Definition)
AMD
和 CommonJS
是最受欢迎的两套模组标准,也各有优缺点,通常开发者会根据自己的需求选择不同的标准。但是,我们也有可能会去使用到不同标准所开发出来的代码,这可能就会造成问题。一种解决方案就是 UMD
,它可以让 AMD
和 CommonJS
使用同一份档案。
ES Module(ESM)
ES6 发布之后,终于实现了模块的功能,并结合了 CommonJS
和 AMD
的两者优点
- 类似
CommonJS
,引入简单的语法如export
和import
,并且也是一个档案为一个模组 - 类似于
AMD
也有支援非同步载入
模组打包工具(Module Bundler)的作用
上个段落介绍了不同模组规范,以及RequireJS 等模组加载工具被创建,让我们能在浏览器中使用模组。然而,虽然有这些模组化工具,因为各家浏览器支援度不同等问题,导致有时候如果你想用别人写好的模组 (例如 npm 上的套件),很可能没办法顺利引用。
因为有浏览器模组机制相容性、兼容性的问题,前端业界后来发展出 webpack 等工具,协助开发者打包模组,所以假如你的代码中,有使用某个 npm 的套件,webpack 会协助你打包,让他变得像是你自己写的模组,这有效解决浏览器模组功能支援度的问题。
除此之外, webpack 这类的不只帮助我们加载管理代码的模组。它进一步扩展到可以支援不同的资源载入,让各种不同资源都可以变成一个模组 (如:图像、字体和样式表)。此外,它可以有其他优化,提供更好的使用者和开发者体验。