模块化的历史
无模块化 ->IIFE(模块化雏形) -> CommonJS(代码层面到框架层面的优化) -> AMD(从同步加载到异步加载的优化) -> CMD(按需加载优化) -> ESModule
0. 无模块化
- 污染全局作用域, 不利于大型项目的开发以及多人团队的共建。
1.IIFE(语法侧的优化)
主要应用的是作用域进行模块化
const iifeModule = ((dependencyModule1, dependencyModule2) => {
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
};
return {
increase,
reset,
};
})(dependencyModule1, dependencyModule2);
iifeModule.increase();
iifeModule.reset();
2. CJS - Commonjs
- node.js 制定
- 特征:
通过 module + exports 去对外暴露接口 通过 require 来调用其他模块
-
模块组织方式main.js 文件
-
优点: CommonJS 率先在服务端实现了,从框架层面解决依赖、全局变量污染的问题
-
缺点:主要针对了服务端的解决方案。对于异步拉取依赖的处理整合不是那么的友好。
// 引入部分
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();
3.AMD
- AMD(Asynchronous module definition)异步的模块定义
- 通过异步加载 + 允许制定回调函数
- 经典实现框架是:require.js
- 通过define来定义一个模块,然后require进行加载
/*
define
params(define的参数): 模块名,依赖模块,工厂方法
*/
define(id, [depends], callback);
require([module], callback);
模块定义方式
define("amdModule", ["dependencyModule1", "dependencyModule2"], (
dependencyModule1,
dependencyModule2
) => {
// 业务逻辑
// 处理部分
let count = 0;
const increase = () => ++count;
const reset = () => {
count = 0;
};
return {
increase,
reset,
};
});
引入模块:
require(["amdModule"], (amdModule) => {
amdModule.increase();
});
在 AMDmodule 中想兼容已有的CJS代码
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('otherModule', [], require => {
const otherModule = require('amdModule');
otherModule.increase();
otherModule.reset();
})
3.UMD
- 兼容 AMD&CJS/如何判断 CJS 和 AMD
(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();
}))(
// 目标是一次性区分CommonJSorAMD
typeof module === "object"
&& module.exports
&& typeof define !== "function"
? // 是 CJS
factory => module.exports = factory(require, exports, module)
// factory 代表要执行的函数 即回调中要执行的函数 将module.exports 等于 要执行的函数
: // 是AMD
define
)
4.CMD
- 按需加载,支持动态引入依赖文件
- 主要应用的框架 sea.js
- 优点:按需加载,依赖就近
- 缺点: 依赖于打包,加载逻辑存在于每个模块中,扩大模块体积
// 引入require
var fs = require('fs'); //同步
require.async('./module3', function (m3) {}) //异步
define("module", (require, exports, module) => {
let $ = require("jquery");
// jquery相关逻辑
let dependencyModule1 = require("./dependecyModule1");
// dependencyModule1相关逻辑
});
5.ES6 模块化
-
引入关键字 —— import
-
导出关键字 —— export
-
优点(重要性):通过一种最统一的形态整合了 js 的模块化
-
缺点(局限性):本质上还是运行时的依赖分析(未进行预编译)
模块引入、导出和定义的地方
// 引入区域
import dependencyModule1 from "./dependencyModule1.js";
import dependencyModule2 from "./dependencyModule2.js";
// 实现代码逻辑
let count = 0;
// 导出区域
// 一种方式
export const increase = () => ++count;
export const reset = () => {
count = 0;
};
// 一种方式
const increase = () => ++count;
const reset = () => {
count = 0;
};
export 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();
动态模块
import("./esModule.js").then((dynamicEsModule) => {
dynamicEsModule.increase();
});