快速了解模块化

226 阅读5分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

背景

学习前端三个月了,准备刷刷面试题,总结总结,一天几道面试题,向大厂进军。

现在随着前端要求越来越高,各种各样的概念也随着而来,我们打包项目或者打包库的时候,一定遇到过我们到底需要打包成什么模块的问题,那我们今天就来了解下这些概念。

问题

AMD,CMD,CommonJS,ES6 Module,UMD,IIFE这些有什么区别?

看到这个题,这都是什么鬼啊,只听说过ES6 Module,其他的都不了解。

前端为什么要使用模块化

image.png

没有模块化,我们以前加载资源是不是都是这样加载。

这样加载有几个缺陷:

  1. 脚本的加载会阻塞DOM 树的渲染。
  2. 脚本与脚本之间引用不方便。
  3. 脚本之间如果有依赖关系,就必须顺序加载,不方便维护。
  4. 发起多次请求。
  5. 基于以上原因,不适合大型应用。

模块化开发,一个模块就是一个实现特定功能的文件,有了模块我们就可以方便的使用别人的代码,需要什么功能就加载什么模块。

模块化的好处:

  1. 避免变量污染,命名冲突。
  2. 提高代码复用率。
  3. 提高维护性。
  4. 依赖关系的管理。

AMD(浏览器端)

Asynchronous Module Definition,中文名是异步模块。它是一个在浏览器端模块化开发的规范,由于不是js原生支持,使用AMD规范进行页面开发需要用到对应的函数库,也就是大名鼎鼎的RequireJS,实际上AMD是RequireJS在推广过程中对模块定义的规范化的产出。

AMD 的代表作require.js

  1. require.js 会自动分析依赖关系,将需要加载的模块正确加载。
  2. requirejs 为全局添加了 define 函数,按照这种约定(AMD)书写模块即可。

调用方式:

image.png image.png

image.png

我们可以看到AMD的写法主要是define,require,依赖必须提前声明好。

CMD(浏览器端)

全称:Common Module Definition。它是一个在浏览器端模块化开发的规范,在Sea.js 中,所有 JavaScript 模块都遵循 CMD规范。

CMD 的代表作sea.js 书写格式如下:

define(factory)

我们来看具体代码:

image.png

image.png

image.png

CMD的特点是:依赖就近编写,动态引入依赖文件。

AMD与CMD的区别

  1. CMD 推崇依赖就近,AMD 推崇依赖前置
  2. 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。

image.png

其实AMD与CMD都是异步加载。

CommonJS(服务端)

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。

代表:nodejs

image.png

image.png

主要写法:module.exports,require 特点:同步加载

UMD

UMD 叫做通用模块定义规范(Universal Module Definition)。也是随着大前端的趋势所诞生,它可以通过运行时或者编译时让同一个代码模块在使用 CommonJs、CMD 甚至是 AMD 的项目中运行。未来同一个 JavaScript 包运行在浏览器端、服务区端甚至是 APP 端都只需要遵守同一个写法就行了。

它集结了CommonJS,CMD,AMD的规范于一身。自适配模块方案

image.png

ES6 Module(服务器和浏览器通用)

ECMAScript2015 规定了新的模块加载方案。

代码使用:

image.png

image.png

image.png

主要写法:export ,import ,依赖提前声明好。

ES6与CommonJS的区别

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
  3. CommonJS 模块的require()是同步加载模块,ES6 模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。

ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的 import 有点像 Unix 系统的“符号连接”,原始值变了,import 加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

IIFE

IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的函数。

写法:

(function(){....})();

js中可以直接使用。

转换ES6 Module

babel

鉴于浏览器支持度的问题,如果要使用 ES6 的语法,一般都会借助 babel

Babel 是一个 JavaScript 编译器。

Babel 只是把 ES6 模块语法转为 CommonJS 模块语法,然而浏览器是不支持这种模块语法的,所以直接跑在浏览器会报错的,如果想要在浏览器中运行,还是需要使用打包工具将代码打包。

webpack

Babel 将 ES6 模块转为 CommonJS 后, webpack 又是怎么做的打包的呢?它该如何将这些文件打包在一起,从而能保证正确的处理依赖,以及能在浏览器中运行呢?

首先为什么浏览器中不支持 CommonJS 语法呢?

这是因为浏览器环境中并没有 module、 exports、 require 等环境变量。

打包工具做的事情就是补充全这些环境变量,类似于垫片的作用。

结语

一步一步慢慢来,踏踏实实把活干!