模块化
文章
- JavaScript 模块 - MDN
- ECMAScript 6 入门 - 阮一峰
- JS 模块规范大盘点:CommonJS, AMD, CMD, UMD 和 ES6 Modules 规范与源码实现 - 知乎
- 说说你对JS的模块化方案的了解 - 前端面试题宝典
什么是前端模块化
- 将复杂程序根据规范拆分成若干模块,一个模块包括输入和输出
- 模块的内部实现是私有的,对外暴露接口与其它模块通信
进化过程
- 全局 function 模式,将不同功能封装成不同函数
缺陷:容易引发全局命名空间冲突
- 全局 namespace 模式,通过对象封装模块
缺陷:外部能够修改模块内部数据
js
window.__Module ={
x:1,
api(){
return{...}
}
}
const m = window.__Module
const a = m.api()
m.x = 2 //模块内应该是私有的,外面不可随意修改
IIFE 模式,通过自执行函数创建闭包
函数作用域与闭包
缺陷:无法解决模块间相互依赖问题
IIFE 模式增强,支持传入自定义依赖
缺陷:
- 多依赖传入时,代码阅读困难
- 无法支持大规模的模块化开发
- 无特定语法支持,代码简陋
CommonJS
node 中的 cjs 模块加载采用同步
加载方式
通过require
加载模块,通过exports
或module.exports
输出模块
特点
- 所有代码都运行在模块作用域,不会污染全局作用域
- 模块可以多次加载,第一次加载时会运行模块,模块输出结果会被缓存,再次加载时,会从缓存结果中直接读取模块输出结果
- 模块加载的顺序,按照其在代码中出现的顺序
- 模块输出的值是值的拷贝,类似 IIFE 方案中的内部变量
require()
绝对路径或相对路径
module.exports
会覆盖掉exports
的结果
ESModule
ESModule
设计理念是希望在编译时就确定模块依赖关系及输入输出
ESM 通过 import 加载模块,通过 export 输出模块
CommonJS 和 ESModule 对比
- CommonJS 模块输出的是值的拷贝,ESM 模块输出的是值的引用
- CommonJS 模块是运行时加载,ESM 模块是编译时输出接口
- CommonJS 是单个值导出,ESM 可以导出多个
- CommonJS 模块是同步加载,ESM 支持异步加载 js
import(xxx).then('...')
- CommonJS 的 this 是当前模块,ESM 的 this 是 undefined
- 二者语法不同
- CommonJS 是 node 默认采用的模块化规范,node 14 后默认支持 ESM;ESM 浏览器默认采用的模块化规范
AMD
Asynchronous Module Definition
,异步模块定义。
require.js
js
define("app.js", ["a.js", "b.js"], function (ajs, bjs) {
var exports = { name: "I am app.js" }
return exports;
}
require(['math'], function(math) {
math.add(2, 3);
})
CMD
Common Module Definition
,通用模块定义。
sea.js
AMD 推崇依赖前置、提前执行,CMD 推崇依赖就近、延迟执行
js
define(function (require, exports, module) {
var a = require('./a')
a.doSomething()
var b = require('./b')
b.doSomething()
})
UMD
Universal Module Definition
,通用模块定义。
UMD 是 AMD 和 CommonJS 的一个糅合。AMD 是浏览器优先,异步加载;CommonJS 是服务器优先,同步加载。
js
;(function (window, factory) {
if (typeof exports === 'object') {
module.exports = factory()
} else if (typeof define === 'function' && define.amd) {
define(factory)
} else {
window.eventUtil = factory()
}
})(this, function () {
//module ...
})