什么是前端模块化

407 阅读5分钟

面试中,关于打包工具的相关知识是非常常见的考题,无可避免的可能会被问到,为什么需要使用到打包工具、或前端工程化的实现,而要回答这些问题前,需要先了解的就是 前端模块化。所以在本篇笔记中,会让大家了解前端模块化的演进和功用。

什么是模块化?

开发人员将代码或系统分割为不同模块的过程,就称为模块化。每个模块就代表一个完整的小程序或小功能,所有模块组装起来成为一个整体,进而完成整个系统的功能要求。

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 中的立即调用函数是什么

JS 模块化规范

CommonJS

Node.js 发布时,它带来了新的挑战。因为 JavaScript 不是在浏览器中运行,因此没有可以添加到其中的 html 文件和脚本标签,那么 Node 应用程序应该如何加载不同的代码呢?- 通过 CommonJSCommonJS 规范在 2009 年被社区开发出来,引入了 requireexport 声明模块的语法,一个文件就是一个模块,是以同步的方式加载模块,因此适合服务器端的开发。

AMD(Async Module Definition)

AMD 在中文是非同步模块定义的意思。它起源于 Dojo 工具箱(一套 JavaScript Web 应用程序库)。同时, AMD 一开始就是为浏览器而设计的,目前为止,最受欢迎的 AMD 实现是 RequireJS (一个 JavaScript 模块加载器)。 AMD 可以自动决定相依关系,模块是以非同步方式加载,避免阻塞的问题,并且可以把多个模块定义在同一个文件中。

CMD(Common Module Definition)

CMD 的出现较晚一些, Sea.js 在 2012 年开始推广,它汲取了 CommonJSAMD 规范的优点,也是专门用于浏览器的异步模块加载。在规范中,一个文件就是一个模块。

UMD(Universal Module Definition)

AMDCommonJS 是最受欢迎的两套模块标准,也各有优缺点,通常开发者会根据自己的需求选择不同的标准。但是,我们也有可能会去使用到不同标准所开发出来的代码,这可能就会造成问题。一种解决方案就是 UMD,它可以让 AMDCommonJS 使用同一份文件。

ES Module(ESM)

ES6 发布之后,终于实现了模块的功能,并结合了 CommonJSAMD 的两者优点

  • 类似 CommonJS,引入简单的语法如 exportimport,并且也是一个文件为一个模块
  • 类似于 AMD 也有支持非同步加载

模块打包工具 (Module Bundler) 的作用

上个段落介绍了不同模块规范,以及 RequireJS 等模块加载工具被创建,让我们能在浏览器中使用模块。然而,虽然有这些模块化工具,因为各家浏览器支持度不同等问题,导致有时候如果你想用别人写好的模块 (例如 npm 上的包),很可能没办法顺利引用。

因为有浏览器模块机制相容性、兼容性的问题,前端业界后来发展出 webpack 等工具,协助开发者打包模块,所以假如你的代码中,有使用某个 npm 的包,webpack 会协助你打包,让他变得像是你自己写的模块,这有效解决浏览器模块功能支持度的问题。

除此之外,webpack 这类的不只帮助我们加载管理代码的模块。它进一步扩展到可以支持不同的资源加载,让各种不同资源都可以变成一个模块 (如:图像、字体和样式表)。此外,它可以有其他优化,提供更好的使用者和开发者体验。