js 模块化历史发展
无模块化
问题
- 污染全局作用域 => 不利于大型项目的开发及多人项目的共建
模块化的雏形 - IIFE(语法侧的优化)
函数包裹,形成局部作用域,匿名函数自调
有额外依赖时,如何优化 IIFE 相关代码
优化 1: 依赖其他模块的 IIFE
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0;
return {
increase: () => ++count;
reset: () => {
count = 0;
}
}
})(dependencyModule1, dependencyModule2);
iifeModule.increase();
iifeModule.reset();
CommonJS
由 node.js 制定
特征:
- 通过 module + exports 去对外暴露接口
- 通过 require 来调用其他模块
- 优点:
CommonJS 率先在服务端实现了,从框架层面解决依赖、全局变量污染的问题 - 缺点:
主要针对了服务端的解决方案。对于异步拉取依赖的处理整合不是那么的友好。
AMD 规范
通过异步加载 + 允许制定回调函数 解决异步依赖的问题
经典实现框架: require.js
- 优点: 适合在浏览器中加载异步模块,可以并行加载多个模块
- 缺点:会有引入成本,不能按需加载
模块定义方式
define("amdModule", ["dependencyModule1", "dependencyModule2"], (
dependencyModule1,
dependencyModule2
) => {
// 业务逻辑
// 处理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
};
return {
increase,
reset,
};
});
CMD 规范
按需加载
主要应用的框架 sea.js
define("module", (require, exports, module) => {
let $ = require("jquery");
// jquery相关逻辑
let dependencyModule1 = require("./dependecyModule1");
// dependencyModule1相关逻辑
});
-
优点:按需加载,依赖就近
-
依赖于打包,加载逻辑存在于每个模块中,扩大模块体积
面试题:AMD&CMD 区别
答:依赖就近,按需加载
ES6 模块化
新增定义:
引入关键字 —— import
导出关键字 —— export
面试题:动态模块
考察:export promise
ES11 原生解决方案:
import("./esModule.js").then((dynamicEsModule) => {
dynamicEsModule.increase();
});
-
优点(重要性):通过一种最统一的形态整合了 js 的模块化
-
缺点(局限性):本质上还是运行时的依赖分析
解决模块化的新思路 - 前端工程化
根本问题 - 运行时进行依赖分析
前端的模块化处理方案依赖于运行时分析
解决方案:线下执行
grunt gulp webpack
工程化实现
step1: 扫描依赖关系表
step2: 重新生成依赖数据模板
step3: 执行工具,采用模块化方案解决模块化处理依赖
优点:
- 构建时生成配置,运行时执行
- 最终转化成执行处理依赖
- 可以拓展