一、为什么要做js模块化
命名重复:前端项目逐渐复杂,需要多人开发多种功能,命名污染全局作用域,不利于开发维护
文件分离:多种不同 功能的文件需要被分到不同文件中实现后再被引用
二、js模块化的方式和发展历程
-iife
const iifeModule = (function(dependencyModule1,dependencyModule2) {
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
return {
increase,decrease,reset
}
})({num:8},dependencyModule2);
iifeModule.increase();
iifeModule.increase();
iifeModule.increase();
iifeModule.increase();
iifeModule.increase();
面试题 了解早期jquery是如何处理依赖以及模块化的吗?/了解iife是如何解决多方依赖的问题的? 答:iife加传参
-Common.js
-使用module.exports={}或者exports.increase=()=>{}去暴露一个模块
//main.js
const dependencyModule1 = require('./dependencyModule1');
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
exports.increase = increase;
exports.decrease = decrease;
exports.reset = reset;
// 或者
module.exports = {
increase,decrease,reset
}
-使用require('/...')引入模块
const {increase,decrease,reset} = require(./main.js);
increase();
decrease();
reset();
-common.js的优缺点:
优点: Common.js率先在服务端实现了模块化,从框架层面解决依赖、全局变量污染的问题
缺点: 主要针对服务端,对客户端和浏览器的异步拉取依赖整合不是那么友好
-AMD(Require.js):允许异步加载依赖,允许指定回调函数
使用define('moduleName',[dependencyModules], callback)定义模块
define('countModule',['dependencyModule1'],dependencyModule1 => {
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
return {
increase,decrease,reset
}
})
使用require([dependencyModules],callback)
require(['countModule'],countModule => {
countModule.increase();
})
面试题: 如果在countModule中想兼容已有代码,怎么办
define('countModule',[],require => {
const dependencyModule1 = require('./dependencyModule1');
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
return {
increase,decrease,reset
}
})
面试题:AMD中使用揭露模式
define('countModule',[],(require,export,module) => {
const dependencyModule1 = require('./dependencyModule1');
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
export.increase = increase;
export.decrease = decrease;
export.reset = reset;
})
-UDP
面试题:兼容AMD&CJS/如何判断CJS和AMD? 答: UDP就是兼容CJS和AMD,使用AMD作为基础,并兼容CJS
const CJSModule = function(require,export,module){
const dependencyModule1 = require('./dependencyModule1');
let count = 0;
const increase = () => {
if(count > dependencyModule1.num) return dependencyModule1.num;
return ++count;
};
const decrease = () => --count;
const reset = () => {this.count = 0}
exports.increase = increase;
exports.decrease = decrease;
exports.reset = reset;
}
const udpModule = (function(factory) {
if(typeof module === "object"&& module.exports&& typeof define !== "function") {
CJSModule(require,export,module);
}else {
define((require,export,module) => {
CJSModule(require,export,module);
})
}
}))(CJSModule)
udpModule()
-AMD的优缺点:
优点: 适合在浏览器中加载异步模块,可以并行加载多个模块
缺点:会有引入成本,不能按需加载
-CMD-按需加载,主要应用与sea.js
define('module', (require, exports, module) => {
let $ = require('jquery');
// jquery相关逻辑
let dependencyModule1 = require('./dependecyModule1');
// dependencyModule1相关逻辑
})
-CMD的优缺点:
优点: 按需加载,依赖就近
缺点:依赖于打包,加载逻辑存在于每个模块中,扩大模块体积
ES6模块化
-引入关键字 —— import
import antd from 'antd';
import React from 'react';
#### 导出关键字 —— export
```js
export default PageHeader;
export {PageHeader}
export function() {}
export const defaultEnum = {}
模板引入的地方
<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();
面试题:动态模块**
考察:export promise
ES11原生解决方案:
import('./esModule.js').then(dynamicEsModule => {
dynamicEsModule.increase();
})
ESModule优缺点
优点(重要性):通过一种最统一的形态整合了js的模块化
缺点(局限性):本质上还是运行时的依赖分析
三、前端工程化(完全体 webpack为核心的工程化 + mvvm框架组件化 + 设计模式)
<!doctype html>
<script src="main.js"></script>
<script>
// 给构建工具一个标识位
require.config(__FRAME_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 = () => {}
})
优点:
- 构建时生成配置,运行时执行
- 最终转化成执行处理依赖
- 可以拓展