共计 5198 个字符,预计需要花费 13 分钟才能阅读完成。
JS 模块化
背景
js 自身简略的页面设计:页面动画 + 表单提交
并无模块化 or 命名空间的概念
JS 模块化需要日益增长
幼年期:无模块化
- 开始须要在页面中减少一些不同的 js:动画、表单、格式化
- 多种 js 文件被分在不同的文件中
- 不同的文件又被同一个模板饮用
// 过后是被认可的:文件拆散是最根底的
<script src="jquery.js"> </script>
<script src="main.js"> </script>
<script src="dep1.js"> </script>
<script src="dep2.js"> </script>
问题:
- 净化全局作用域 => 不利于大型项目的开发以及多人团队的共建
成长期:模块化的雏形 – IIFE(立刻函数、语法侧的优化)
作用域的把控
例子:
// 定义一个全局变量
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
increase()
reset()
利用函数的块级作用域
(() => {
let conut = 0
// ...
})
仅定义了一个函数,并没有执行,如果立刻执行
(() => {
let conut = 0
// ...
})()
初步实现了一个最简略的模块(通过立刻执行函数)
尝试去定一个最简略的模块
const iifeModule = (() => {
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
return {
// 代码块 1
increase: () => ++count,
// 代码块 2
reset: () => {count = 0}
}
})()
iifeModule.increase()
iifeModule.reset()
诘问:如果有额定依赖,如何优化 iife?– 参数的调用
优化 1:依赖其余模块的 IIFE
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0
return {
// 代码块 1
increase: () => ++count,
// 代码块 2
reset: () => {count = 0}
}
})(dependencyModule1, dependencyModule2)
iifeModule.increase()
iifeModule.reset()
问:理解 jquery 的依赖解决以及模块加载计划吗?/ 理解传统 IIFE 是如何解决多方依赖的问题吗?
答:IIFE + 传参调配
实际上,jquery 等框架其实利用了 revealing module(揭示模式) 的写法
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
// 仅裸露接口,不裸露办法
return {
increase,
reset
}
})(dependencyModule1, dependencyModule2)
iifeModule.increase()
iifeModule.reset()
成熟期
CJS – CommonJS
由 node 制订的一套计划
特色:
- 通过 module + exports 去对外裸露借口
- 通过 require 来调用其余模块
模块组织形式
main.js 文件中
// 援用
const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)
// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
// 做一些跟引入依赖相干事宜……
// 裸露接口
exports.increase = increase
exports.reset = reset
module.exports = {
increase,
reset
}
模块应用形式
const {increase, reset} = require(main.js)
increase()
reset()
可能被问到的货色
理论执行解决
转化成立即执行函数
CJS 和 IIFE 的联合
// 须要穿参
(function () {const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)
// 业务逻辑……
}).call(thisValue, exports, require, module)
CJS 长处:
CommonJS 率先在服务端实现了,从框架层面解决依赖、全局变量净化的问题CJS 毛病:
次要针对了服务端的解决方案,都是同步引入。对于异步拉取依赖整合没有那么敌对。
新的问题 —— 异步依赖
AMD 标准
通过异步加载 + 容许定制回调函数
经典实现框架:require.js
新增定义形式:
// 通过 deifine 来定义一个模块,而后 require 进行加载
/**
* id: 模块名
* [depends]:依赖模块
* callback:工厂办法
*/
define(id, [depends], callback)
require([module], callback)
模块定义形式:
define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1,dependencyModule1) => {
// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
return {increase, reset}
})
引入模块:
require(['amdModule'], amdModule => {amdModule.increase()
})
问:如果在 amdModule 中想兼容已有代码,怎么办?
define('amdModule', [], require => {
// 援用
const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)
// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
// 做一些跟引入依赖相干事宜……
// 裸露接口
exports.increase = increase
exports.reset = reset
module.exports = {
increase,
reset
}
})
问:AMD 中应用 revealing
define('amdModule', [], (require, exports, module) => {
// 援用
const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)
// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
// 做一些跟引入依赖相干事宜……
// 裸露接口
exports.increase = increase
exports.reset = reset
})
define(require => {const otherModule = require('amdModule')
otherModule.increase()
otherModule.reset()})
问:兼容 AMD & CommonJS?答:UMD
UMD 的呈现
(define('amdModule', [], (require, exports, module) => {
// 援用
const dependencyModule1 = require(dependencyModule1)
const dependencyModule2 = require(dependencyModule2)
// 解决
let count = 0
// 代码块 1
const increase = () => ++count
// 代码块 2
const reset = () => {count = 0}
// 做一些跟引入依赖相干事宜……
// 裸露接口
exports.increase = increase
exports.reset = reset
}))(
// 指标是一次性辨别 CommonJS or AMD
typeof module === "object"
&& module.exports
&& typeof define !== "function"
? // 是 CommonJS
factory => module.exports = factory(require, exports, module)
: // 是 AMD
define
)
- 长处:适宜在浏览器中加载异步模块,能够并行加载多个模块
- 毛病:会有引入老本,不能按需加载
CMD 标准
按需加载
次要利用的框架 sea.js
define('module', [], (require, exports, module) => {let $ = require('jquery');
// jquery 相干的代码
let dependencyModule1 = require('dependencyModule1')
// dependencyModule1 相干逻辑
})
- 长处:按需加载,依赖就近
- 毛病:依赖打包,加载逻辑存在每个模块中,扩充模块体积
ES6 模块化
走向新时代
新增定义:
引入关键字 —— import
导出关键字 —— export
模块引入、导出和定义
esModule.js
// 引入
import dependencyModule1 from 'dependencyModule1'
import dependencyModule2 from 'dependencyModule2'
// 实现
let count = 0
export const increase = () => ++count
const reset = () => {count = 0}
// 导出
export default {reset}
模板引入的中央:
<script type="module" src="esModule.js"></script>
node 中:
import {reset} from 'esModule.js'
reset()
import esModule from 'esModule.js'
esModule.reset()
- 长处(重要性):通过一种最对立的状态整合 js 模块化
- 毛病(局限性):实质上还是运行了依赖剖析
解决模块化的新思路 —— 前端工程化
背景
基本问题 — 运行时进行依赖剖析
前端的模块化解决计划依赖于运行时剖析
解决方案:线下执行
grunt、gulp、webpack
<!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');
exports.run = () => {// run}
})
工程化实现
step1: 扫描依赖关系表:
{a: ['b', 'c']
b: ['d']
}
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'], () => {
// 执行代码
exports.run = () => {// run}
})
长处:
- 构建时生成配置,运行时执行
- 最终转化执行解决依赖
- 能够扩大
齐全体:webpack 为外围的工程化 + MVVM 框架组件化 + 设计模式
总结:
- 无模块化 -> IIFE => 从无到有的过程
- IIFE -> CommonJS => 从代码层到框架层
- CommonJS -> AMD => 解决了异步问题
- AMD -> CMD => 解决了按需加载的问题