该文章主要讲述了前端模块化的发展历史和各个阶段的技术方案,包括无模块化(IIFE)、CommonJS、AMD、CMD、ESModule、UMD。
其中,无模块化时期的文件拆分是最基础的模块化,但也存在函数命名冲突的问题;
IIFE 是现代模块化的基石,利用函数的块级作用域进行隔离,可以控制作用域;
CommonJS 文件即模块,模块加载同步,适用于服务器端 node,浏览器端使用 webpack 或 browserfy。
最后,各种模块化技术方案都是为了更好地满足前端代码管理、组织、通信的需求,模块已经成为了代码管理/编译、业务分离的基本单元。
总的发展历史:
需求:
文件拆分是最基础的模块化
<script src='jq.js'></script>
<script src='main.js'></script>
<script src='dep1.js'></script>
// ...
问题:这个时期函数命名可能会冲突,影响到其他人写的代码
总结:
主要是对标签下载和执行时机的控制
ESM
默认是通过 defer
的方式加载的,所以是不需要在 script
标签上加 defer
属性的IIFE 主要是开始对作用域的把控
利用函数的块级作用域进行隔离
可以说 IIFE 是现代模块化的基石
(function ($) {
console.log($);
return {
data: [],
};
})(jQuery); //注入对象
// a.js
var name = 'morrain';
var age = 18;
exports.name = name;
exports.getAge = function () {
return age;
};
// b.js
var a = require('a.js');
console.log(a.name); // 'morrain'
a.name = 'rename';
var b = require('a.js');
console.log(b.name); // 'rename'
// a.js
var name = 'morrain';
var age = 18;
exports.name = name;
exports.age = age;
exports.setAge = function (a) {
age = a;
};
// b.js
var a = require('a.js');
console.log(a.age); // 18
a.setAge(19);
console.log(a.age); // 18
AMD(Asynchronous module definition)异步的模块定义
解决了 Commonjs 不支持异步的缺点,可以在浏览器端运行
经典代表:require.js
使用方法:
// define来定义模块
define(id, [depends], callback);
// require进行加载
require([module], callback);
示例:
//提前加载执行顺序
// RequireJS
define('a', function () {
console.log('a load');
return {
run: function () {
console.log('a run');
},
};
});
define('b', function () {
console.log('b load');
return {
run: function () {
console.log('b run');
},
};
});
require(['a', 'b'], function (a, b) {
console.log('main run'); //
a.run();
b.run();
});
// a load
// b load
// main run
// a run
// b run
缺点:
define('amdModule', [], (require) => {
const dep1 = require('./dep1');
const dep2 = require('./dep2');
// 业务逻辑……
});
CMD(Common Module Definition-通用模块定义)推崇依赖后置,也就是按需执行
CMD 解决了 AMD依赖前置导致的引入成本过高的问题
整合了 CJS 和 AMD 的特点,浏览器端运行
经典代表:Sea.js
// 引入require
var fs = require('fs'); //同步
require.async('./module3', function (m3) {}); //异步
// sea.js,按需引入
define('a', function (require, exports, module) {
console.log('a load');
exports.run = function () {
console.log('a run');
};
});
define('b', function (require, exports, module) {
console.log('b load');
exports.run = function () {
console.log('b run');
};
});
define('main', function (require, exports, module) {
console.log('main run');
var a = require('a');
a.run();
var b = require('b');
b.run();
});
seajs.use('main');
// main run
// a load
// a run
// b load
// b run
缺点:
UMD (Universal Module Definition)就是一种通用模块定义规范,让你的模块能在所有运行环境中使用,如CommonJS
, AMD
, CMD
(function (root, factory) {
if (typeof module === 'object' && typeof module.exports === 'object') {
console.log('是commonjs模块规范,nodejs环境');
module.exports = factory();
} else if (typeof define === 'function' && define.amd) {
console.log('是AMD模块规范,如require.js');
define(factory);
} else if (typeof define === 'function' && define.cmd) {
console.log('是CMD模块规范,如sea.js');
define(function (require, exports, module) {
module.exports = factory();
});
} else {
console.log('没有模块环境,直接挂载在全局对象上');
root.umdModule = factory();
}
})(this, function () {
return {
name: '我是一个umd模块',
};
});
ESModule 是伴随着 ES6 推出的原生模块化解决方案
import 输入、export 输出
chunk
和tree shaking
import().then()
import.meta
获取模块元数据