JS 模块化
本节课是基础和工程化之间的模块化
基础 => 模块化 => 工程化
1. 历史
背景
JS 最开始定位:简单页面设计支撑 —— 页面的动画+表单提交
并无模块化 or 命名空间基础
JS 的模块化需求日益增长
幼年期:无模块化
- 开始需要页面加载不同的 js 库:动画库、表单库、格式化工具
- 多种 js 文件被分在不同的文件中
- 不同的文件又被同一个模板引用
1 2 3 4 5
| <script src='tools.js'></script> <script src='map.js'></script>
<script src='main.js'></script>
|
- 追问
script 标签的两个参数- async & defer
1
| <script src='tools.js' type='text/javascript' async></script>
|
总结:
普通:解析到标签,立刻 pending,并且下载执行
defer:解析到标签,开始异步下载,继续往后进行,完成之后开始执行
async:解析到标签,开始异步下载,下载完成之后立刻执行并且阻塞往后解析,执行完成后再继续向下
问题导向:浏览器渲染原理、同步异步原理、模块化加载原理
问题出现
- 污染全局作用域 => 不利于大型项目的开发与多人团队的构建
成长期:模块化的雏形 - IIFE(语法侧的处理)
作用域的把控
举例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| let count = 0
const increase = () => ++count
const reset = () => { count = 0 }
increase() reset()
|
利用函数的独立作用域
1 2 3 4
| ;(() => { let count = 0 })()
|
定义的函数 + 立即执行 => 独立的空间
初步实现了一个基础模块
尝试定义一个简单的模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| cosnt module =(()=>{ let count =0; return{ increase:()=>++count; reset:()=>{ count =0; } } })(); module.increase(); module.reset();
const iifeModule = ((dependencyModule,dependencyModule2)=>{ let count =0; return{ increase:()=>++count; reset:()=>{ count =0; } })(dependencyModule,dependencyModule2);
const iifeModule = ((dependencyModule,dependencyModule2)=>{ let count =0; const increase = () => ++count;
const reset = () => { count = 0; }
return{ increase, reset })(dependencyModule,dependencyModule2);
|
- 面试方向
- 深入模块化实现
- 转向框架 jquery vue react 模块组件的实现细节,以及框架原理特征
- 转向设计模式 - 注重模块化的设计
成熟期
Cjs - commonjs
node.js 指定的标准
特征:
- 通过 module + export 去外暴露接口
- 通过 require 去调用其他模块
模块的
dep.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const dependencyModule=require('..dependencyModule')
let count =0; const increase = () => ++count;
const reset = () => { count = 0; }
export.increase = increase; export.reset = reset;
module.exports={ increase, reset }
|
// 引入并使用
1 2 3
| const { increase, reset } = require('dep.js') increase() reset()
|
- 优点:
Cjs 服务侧的角度解决了依赖全局污染的问题 + 完全在写法上也实现了主观感受上的模块化
- 缺点:
针对服务端 => 异步
AMD
通过异步加载 + 允许定制回调函数
经典的实现框架:require.js
新增定义方式
1 2 3
| define(id, [depends], callback) requiere([module], callback)
|
1 2 3 4 5 6 7 8 9 10 11 12
| define('amdModule', ['dependencyModule1', 'dependencyModule2'], (dependencyModule1, dependencyModule2) => { let count = 0 const increase = () => ++count
const reset = () => { count = 0 } })
|
1 2 3
| require(['amdModule'], (amdModule) => { amdModule.increase() })
|
面试题
1 2 3 4 5
| define('amdModule', [], (require) => { const dependencyModule1 = require('./dependencyModule1') const dependencyModule2 = require('./dependencyModule2') })
|
面试:区分 Cjs 和 AMD?手写兼容 Cjs 和 AMD 的模块
UMD
优点:解决了服务、客户端异步动态依赖问题
缺点:引入成本,未考虑按需加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| (define("amdModule", ["dependencyModule1", "dependencyModule2"], ( dependencyModule1, dependencyModule2 ) => { let count = 0; const increase = () => ++count;
const reset = () => { count = 0; }; export.increase =increase; export.reset = reset; return{ increase, reset } }))( typeof module === 'Object' && module.exports && typeof define !== 'function' ? factory => module.exports => factory(require,exports,module) : define )
|
CMD 规范
按需加载
主要应用框架:sea.js
1 2 3 4 5 6 7 8 9 10 11
| define('module', (require, exports, module) => { let $ = require('map') if (xxx) { return }
let depends2 = require('./dependencyModule2') })
|
优点:
缺点:
完全体 ESM => ES Module
走进新时代
新增定义:
引入 - import
导出 - export
1 2 3 4 5 6 7 8 9 10
| import dependencyModule1 from './dependencyModule1'
export default { increase, reset, }
|
追问:
处理动态异步依赖
1 2 3 4 5 6 7 8 9 10
| const moduleDependency = async () => { return await dependencyModule1.init() }
import('./dependencyModule.js').then((dynamicEsModule) => { dynamicEsModule.init() })
|
优点
函数式编程
1 2 3 4 5 6 7 8 9 10 11
| const fetch = ajax(method, url, params)
const fetch = ajax.get(url)
const request = fetch(params)
const fetch = ajax(method)(url)(params)
send(request, fetch)
|