前端模块化
在vite(rollup)的打包配置里有一个format的选项,可以指定最终打包产物的格式,默认情况下是ES module,但是不同的场景可能会有所区别,本文就想通过这个来讲讲前端模块化。
IIFE
自执行函数 > 英文全称:immediately invoked function expressions
适用场景 浏览器中带有<script>标签包裹的代码块,简单来说就是普通的业务应用都可,没啥兼容问题
优点
- 函数作用域避免了全局变量的污染 iife也算是比较早期的模块化方案了,只需对外暴露一个全局变量,最知名的就是
jQuery
CJS
真正的模块化解决方案,最早从Node应用开始 > 全称:CommonJS
关键词:module,exports,global,require
特点
- 模块加载
require就是代码执行 - 所有代码运行在模块作用域中,不污染全局
- 模块可以多次加载,但是只在第一次加载运行,后面是缓存
适用场景 Nodejs,浏览器端需要用其他打包工具支持
AMD
异步模块定义
> 全拼:Asynchronous module definition
amd可以理解是CommonJs在浏览器端的解决方案,cjs在服务端是同步加载依赖代码的,因为服务端都是本地磁盘读取文件,没有网络开销,速度很快,浏览器端要是也这么干,很有可能因为依赖在远程,因网络时间开销而导致出现“假死”,所以浏览器端采用异步加载模块的方式。
关键词:require,define
require的第一个参数是需要异步加载的依赖列表,第二个是模块异步加载后的执行代码,define第一个参数是模块的依赖模块,第二个是模块代码
优点
- 体积更小,代码按需加载
- 不堵塞js线程运行
UMD
联合模块定义
> 全拼:Universal Module Definition
准确来说并不是一个独立的模块标准,而是集合了cjs,amd,iife等一体的打包模式,会自动判断当前可用环境。
优点:
- 一套代码,多端使用,模块化兼容性好
一般会在webpack中成为备用模块
ESM
ES6标准中的模块规则,一统浏览器和服务端标准的解决方案
> 全拼:ES6 Module
关键字:import,export
适用场景: 结合其他打包工具(webpack)使用或浏览器<script type="module">标签包裹
特点:
- 浏览器端会异步加载,延续AMD的优势,但利用了浏览器原生的解析能力,代码体积更小
- 模块内自动采用严格模式
- 模块中的this指向并不是window或global,而是
undefined
和cjs的三个差异:
- cjs 模块输出的是一个值的拷贝(浅拷贝),esm输出的是值的引用。
- cjs 模块是运行时加载,esm是编译时输出接口。
- cjs同步加载模块,esm异步加载,有一个独立的模块依赖的解析阶段。
在cjs中,如果多个地方都用了同一个模块,对这个模块进行修改,其实不会有影响,但是在esm中,如果你用incCounter修改了counter值,其他业务模块调用counter的时候就会有变化
原因是esm中模块加载的时候还没完全去取值,实际业务代码执行到了才会去拿真正的值,import的时候只是建了个具体地址,留着后面调用时候方便。