理解模块化
模块化就是 把复杂的系统分解到多个模块以方便编码。
过去代码组织方式,会出现的问题:
- 命名空间冲突
- 无法合理地管理项目依赖和版本
- 无法方便控制依赖的加载顺序
- 项目体积变大后难以维护
模块化的发展
函数即模块
不同的函数声明,就是一个模块。
缺点:
- 污染全局变量,可能与其他模块发生变量名冲突
- 模块成员之间依赖关系,难以直接看出来 ```javascript function getUserList() {}
function updateUserName() {}
<a name="pbw7B"></a>
## 对象即模块
为了解决污染全局变量的问题,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。<br />优点:
1. 减少全局的变量数目
2. 一定程度上,避免了变量名冲突
缺点:
1. 对象会暴露所有模块成员,内部状态可被外部改写。(模块成员是非私有的)
```javascript
var todo = {
list: [],
add: function() {},
delete: function() {},
}
var user = {
list: [],
add: function() {},
delete: function() {},
}
立即执行函数(IIFE)
利用 IIFE 创建闭包,保存私有变量,只暴露给外部对应可访问的权限。
优点:
- 避免变量名冲突
- 拥有私有变量
缺点:
- 依赖情况依然不清晰
var todo = (function () {
const list = []
return {
add: function (item) {
list.push(item)
},
delete: function (index) { },
get: function (index) { }
}
})()
IIFE 增强版(引入依赖)
IIFE 增强版:引入依赖
- 这就是现代模块实现的基石;
```javascript
var _fetch = (function () {
return function ({ method, url, data, callback }) {
return new Promise((resolve, reject) => {
}) } })()const XHR = new XMLHttpRequest();
XHR.open(method, url)
XHR.onload = function () {
resolve(XHR.responseText)
callback && callback(XHR.responseText)
}
XHR.send(data)
var todo = (function (dependencies) { const { _fetch } = dependencies
return {
getTodo: function (name) {
return new Promise((resolve, reject) => {
_fetch(‘get’, https://xxx?name=${name}
)
.then(res => {
resolve(res)
})
})
}
}
})({ _fetch })
<a name="dtgLu"></a>
# 模块化方案
在**模块化编程**中,开发者将程序分解成离散功能块(discrete chunks of functionality),并称之为 **模块**。<br />每个模块具有比完整程序更小的接触面,使得校验、调试、测试轻而易举。<br />精心编写的模块提供了可靠的抽象和封装界限,使得应用程序中每个模块都具有条理清楚的设计和明确的目的。
- CommonJS
- **同步加载** 依赖的模块
- 可复用于 Node.js 环境 ,例如做同构应用
- 成熟的第三方模块社区
- 无法直接运行于浏览器环境,必须通过工具转换为标准的 ES5
- AMD (Asynchronous Module Definition)
- **异步加载** 依赖的模块
- **并行** 加载多个模块
- 可在不转换代码的情况下直接在 **浏览器** 运行
- 可运行在浏览器和 Node.js 环境
- 代表性实践 [requirejs](http://requirejs.org/)、[curl.js](https://github.com/cujojs/curl)
- CMD(Common Module Definition)
- CMD 规范整合了 CommonJS 和 AMD 规范的特点
- CMD 规范专门用于**浏览器端**,模块的加载是异步的,模块使用时才会加载执行
- 代表性实践:[sea.js](https://github.com/seajs/seajs)
- ES6 Module
- 语言层面上实现模块化
- 需转换成 ES5 实现
- Module in CSS
- **@import **语句
![image.png](https://cdn.nlark.com/yuque/0/2022/png/1174243/1647584418885-a201be8e-5a43-4f78-8598-9d6c32ed4f56.png#clientId=u5ee63d15-2b19-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ueae51822&margin=%5Bobject%20Object%5D&name=image.png&originHeight=569&originWidth=720&originalType=url&ratio=1&rotation=0&showTitle=false&size=170462&status=done&style=none&taskId=ucab38ad4-f9f6-47bd-b99f-d2b3020334d&title=)
<a name="EK6Qq"></a>
## 手写模块管理引擎
注意:不可以在 node.js 环境下运行,因为 node.js 内置了 module 变量声明,会冲突。
```javascript
/**
* 简易的模块管理引擎
* 同步执行
*/
const module = (function () {
// 模块容器
const moduleList = {}
/**
* 定义模块
* @param {String} name 模块名称
* @param {Array} modulesName 依赖模块名称
* @param {Function} action 模块执行函数
*
*/
function define(name, modulesName, action) {
const modules = modulesName.map((name, i) => moduleList[name])
moduleList[name] = action.apply(null, modules)
}
return { define }
})()
// 下面都是在使用模块管理
/**
* 声明一个找最大值的模块
* 参数一:模块名称
* 参数二:该模块依赖的其他模块
* 参数三:该模块的执行函数
*/
module.define('findMax', [], function () {
return {
max(arr) {
let res = 0
arr.forEach(item => res = Math.max(res, item))
return res
}
}
})
/**
* 声明 lesson 一个模块,它依赖于找最大值的模块
*/
module.define('lesson', ['findMax'], function (konsoue) {
let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
const max = konsoue.max(data)
console.log(max);
return data
})
/**
* a 模块,我们用它修改 lesson 模块的值
*/
module.define('a', ['lesson'], function (lesson) {
lesson[0] = 4
})
/**
* b 模块,由于我们在 a 修改了 lesson 模块的值
* 在 b 模块,访问 lesson 模块的值是已经被改变的了
* 也就是说,各个模块,引用的同一个模块,确实是单例模式
*/
module.define('b', ['lesson'], function (lesson) {
console.log(lesson);
})