一、无模块化
1、开始需要在页面中增加一些不同的js
2.多种js文件被分在不同的文件种
3.不同的文件又被同一个模板引用
例:
```js
<script src="jquery.js"></script>
<script src="main.js"></script>
<script src="dep.js"></script>
```
文件分离是最基础的模块化第一步
问题:
*污染全局作用域 =》不利于大型项目的开发以及多人团队的共建
二、模块化雏形—— IIFE (语法侧的优化)
本质: 作用域的把控
例:
//定义一个全局变量
let count = 0
const increase = () => ++count;
const reset = () =>{
count = 0
}
increase();
reset();
利用函数块级作用域
(() =>{
let count =0;
})();
//在这里仅定义了一个函数,立即执行
接下来定义一个最简单的模块
const iifeModule = (()=>{
let count = 0;
return {
increase: () => ++count;
reset: () =>{
count = 0
}
}
})();
iifeModule.increase();
iifeModule.reset()
问题1: 有额外依赖时,如何优化IIFE相关代码?
优化:依赖其他模块的IIFE
//将其他模块当做参数进行传递
const iifeModule = ((dependencyModule1,dependencyModule2)=>{
let count = 0;
return {
increase: () => ++count;
reset: () =>{
count = 0
}
}
})(dependencyModule1,dependencyModule2);
iifeModule.increase();
iifeModule.reset()
三、CJS - CommonJS(node.js 制定)
特征:
- 通过module + exports 对外暴露接口
- 通过require 来调用其他模块
模块组织方式 main.js
//引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
//处理部分
let count = 0;
const increase = () => ++count;
const reset = () =>{
count = 0;
}
...
//暴露接口部分
exports.increase = increase;
exports.reset = reset;
module.exports = {
increase, reset
}
模块使用方式
const { increase, reset } = require('./main,js')
increase();
reset();
实际执行处理
(function(thisValue, exports, require, module) {
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
//业务逻辑
...
}).call(thisValue, exports, require, module);
优点:
- CommonJS率先在服务器端实现了从框架层面解决依赖、全局变量污染的问题
缺点:
- 主要针对了服务端的解决方案。对于异步拉取依赖的处理整合不友好。
新的问题 —— 异步依赖
四、AMD规范
通过异步加载 + 允许制定回调函数
经典实现框架:require.js
新增定义方式:
//通过define 来定义一个模块, 然后require 进行加载
/*
define
params: 模块名, 依赖模块, 工厂方法
*/
define(id, [depends], callback);
require([module], callback);
模块定义方式
//定义一个amdModule 的模块,依赖dependencyModule1 和dependencyModule2 模块
define('amdModule',
['dependencyModule1','dependencyModule2'],
(dependencyModule1,dependencyModule2))=>{
//业务逻辑
//处理部分
let count = 0;
const increase = () => ++count;
const reset = () =>{
count = 0
}
return {
increase, reset
}
})
引入模块
require(['amdModule'], amdModule =>{
amdModule.increase();
})
问题2: 如果在amdModule中想兼容已有代码,如何处理
//define中提供了require方法
define(require =>{
//引入部分
const dependencyModule1 = require('./dependencyModule1');
const dependencyModule2 = require('./dependencyModule2');
//处理部分
let count = 0;
const increase = () => ++count;
const reset = () =>{
count = 0;
}
return {
increase, reset
}
})
问题3: AMD中使用revealing
define('amdModule', [], (require, export, module) =>{
//引入部分
const dependencyModule1 = require('./dependencyModule1');
const dependencyModule2 = require('./dependencyModule2');
//处理部分
let count = 0;
const increase = () => ++count;
const reset = () =>{
count = 0;
}
export.increase = increase();
export.reset = reset();
})
define('amdModule', [], require =>{
const otherModule = require('amdModule');
otherModule.increase();
otherModule.reset();
})
问题4: 兼容AMD & CJS
UMD的出现
(define('amdModule', [], (require, export, module) =>{
//引入部分
const dependencyModule1 = require(./dependencyModule1);
const dependencyModule2 = require(./dependencyModule2);
//处理部分
let count = 0;
const increase = () => ++count;
const reset = () =>{
count = 0;
}
export.increase = increase();
export.reset = reset();
}))(
//区分CommonJS or AMD
typeof module === "object"
&& module.exports
&& typeof define !== "function"
//判断是否为CJS
? //是JS
factory =>module.exports = factory(require, export, module)
: //是AMD
define
);
优点:
- 适合在浏览器中加载异步模块,可以并行加载多个模块
缺点:
- 会有引入成本, 不能按需加载
五、CMD规范
主要应用的框架 sea.js
define('module', (require, export, module)=>{
let $ = require('jueqry');
let dependencyModule1 = require('./dependencyModule1')
})
优点:
- 按需加载, 依赖就近 缺点:
- 依赖于打包, 加载逻辑存在于每个模块中, 扩大模块体积
六、ES6模块化
新增定义:
引入关键字 —— import
导出关键字 —— export
模块引入、导出 和定义的地方
//引入部分
import dependencyModule1 from './dependencyModule1';
import dependencyModule2 from './dependencyModule2';
//代码逻辑部分
let count = 0;
//导出方法1
exports const increase = () => ++count;
exports const reset = () =>{
count = 0;
}
...
//导出方法2
exports default{
increase,reset
}
模板引入
<script type="module" src="esModule.js"></script>
//node中
import {increase, reset} from './esModule.mjs'
increase();
reset()
//或
import esModule from './esModule.mjs'
esModule.increase();
esModule.reset()
问题6: 动态模块
ES11原生解决方案:
import('./esModule.mjs').then(dymaicEsModule =>{
dymaicEsModule.increase
})
七、模块化新思路 —— 前端工程化
根本问题 —— 运行时进行依赖分析(前端的模块化处理方案依赖于运行时分析)
解决方案 —— 线下执行(grunt gulp webpack)
<!doctype html>
<script src="main.js"></script>
<script>
// 给构建工具一个标识位
require.config(_FRAM_CONFIG_);
</script>
<script>
require(['a','e'],()=>{
//业务处理
});
</script>
</html>
define('a',()=>{
let b = require('b');
let c = require('c');
export.run = (){
//run
}
})
工程化实现 (处理思路)
step1: 扫描依赖关系表:
{
a:['b','c'],
b:['d'],
e:[]
}
step2: 重新生成依赖数据模板
<!doctype html>
<script src="main.js"></script>
<script>
// 构建工具生成数据
require.config({
"deps":{
a:['b','c'],
b:['d'],
e:[]
}
});
</script>
<script>
require(['a','e'],()=>{
//业务处理
});
</script>
</html>
step3: 执行工具, 采用模块化方案解决模块化处理依赖
define('a',['b','c',],()=>{
//执行代码
export.run = (){
//run
}
})
优点
- 构建时生成配置,运行时执行
- 最终转化成执行处理依赖
- 可以拓展