1、CommonJs 规范
- 每个文件都是一个独立模块,特性是
运行时加载、同步加载、值的拷贝
- 服务器端(Node)
天然支持 CommonJS 规范
- 浏览器端
需要提前使用 Browserify 编译打包(处理require、exports)
- 模块内属性
-
module 对象
module.id
模块的识别符,通常是带有绝对路径的模块文件名。module.filename
模块的文件名,带有绝对路径。module.loaded
返回一个布尔值,表示模块是否已经完成加载。module.parent
返回一个对象,表示调用该模块的模块。module.children
返回一个数组,表示该模块要用到的其他模块。module.exports
对外的输出接口
-
require
加载其它模块
, 实际上就是读取 module.exports 变量
require.resolve()
:将模块名解析到一个绝对路径require.main
:指向主模块require.main === module
require.cache
:指向所有缓存的模块require.extensions
:根据文件的后缀名,调用不同的执行函数
-
exports 是 module.export 的别名( var exports = module.exports; )
- 只能:exports.add = function (r){return Math.PI * r *r};
- 不能:exports = function(x){console.log(x)};
-
__filename
-
__dirname
-
模块的加载机制值的拷贝
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
// main.js
var counter = require('./lib').counter;
var incCounter = require('./lib').incCounter;
console.log(counter); // 3
incCounter();
console.log(counter); // 3
2、AMD 规范
- 用于
浏览器环境
、结合requireJs
库使用 异步加载模块
// html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- src :requireJS 的入口 -->
<!-- data-main :main文件必须在根目录下,指定网页程序入口文件/主模块 -->
<script src="js/libs/require.js" data-main="main.js"></script>
</head>
<body></body>
</html>
// main.js
(function () {
require.config({
baseUrl: "js/", // 可以指定base路径
paths: {
alerter: "modules/alerter", // js/modules/alerter
dataService: "modules/dataService", // js/modules/dataService
// jquery 天然支持 AMD 规范,内部定义了 define( "jquery"...
// if ( typeof define === "function" && define.amd ) {
// define( "jquery", [], function() {
// return jQuery;
// } );
// }
jquery: "libs/jquery.min", // js/modules/dataService
},
});
// 引入 alerter 模块
require(["alerter"], function (alerter) {
alerter.showMsg();
});
})();
// modules/alerter.js
// 定义一个有依赖的模块:引入 dataService 模块
define("alerter", ["dataService", "jquery"], function (dataService, $) {
let msg = "alerter";
function showMsg() {
$("body").css("background", "red");
console.log(msg, dataService.getName());
}
// 暴露模块
return { showMsg };
});
// modules/dataService
// 定义一个没有依赖的模块
define("dataService", [], function () {
let name = "dataService";
function getName() {
return name;
}
// 暴露模块
return { getName };
});
3、CMD 规范
- 用于
浏览器环境
结合sea.js
库使用 动态加载模块
- 依赖模块加载完成后并不执行、遇到 require 时才执行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
// 入口文件
seajs.use("./js/modules/main.js");
</script>
</head>
<body></body>
</html>
// modules/main.js - 主入口文件
define(function (require, exports, module) {
let module1 = require("./module1.js");
console.log(module1.getName());
let module4 = require("./module4.js");
console.log(module4.getName());
});
// modules/module1.js - 定义没有依赖的模块
define(function (require, exports, module) {
let name = "module1";
function getName() {
return name;
}
// 暴露模块
module.exports = { getName };
});
// modules/module2.js
define(function (require, exports, module) {
let name = "module2";
function getName() {
return name;
}
// 暴露模块
module.exports = { getName };
});
// modules/module3.js
define(function (require, exports, module) {
let name = "module3";
function getName() {
return name;
}
// 暴露模块
module.exports = { getName };
});
// modules/module4.js
define(function (require, exports, module) {
let name = "module4";
function getName() {
return name;
}
// 同步引入 module2
let module2 = require("./module2");
console.log(module2.getName());
// 异步引入 module3
require.async("./module3", function (module3) {
console.log(module3.getName());
});
// 暴露模块
module.exports = { getName };
});
4、ESM 规范
- 浏览器端:使用 Babel 将 ES6+ 转为 ES5、使用 Browserify 编译打包js
- 通常直接使用 webpack、vite 等打包工具处理使用
export:输出的必须是一个 对外接口( 比如 变量 )
- export { m, fn } :导出的并非自变量对象
- import { m, fn } :引用时也并非是结构赋值
- 内部值改变,导致外面引用值的变化
- 外部不能修改,导出的来的值(报错)
// 导出的并非自变量对象( 固定写法 )
// 若导出对象,使用 export default { m, fn }
export var m = 1; // 带名字变量
export function fn() {} // 带名字函数
export class Person {} // 带名字类
export { m, fn, Person }; // 同上
// 引用时也并非是结构赋值
import * as axios from "./profile.js";
export default:模块指定默认输出
- 一个模块 export default 命令只能使用一次
- 本质上输出一个叫 default 的变量或方法
- export default 的本质是将后面的值赋给 default
//导出
export default a; // 变量的值
export default { name: 'zs', age: 20 } // 变量的值
export default function(){} // 匿名函数
import()
- import() 函数可以用在 任何地方
- 返回一个 Promise 对象
- import()函数与所加载的模块没有静态连接关系
<script type="module">
import("./module/a.js").then(res=>{})
</script>
// 条件加载
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
// 按需加载
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
// 同时导入多个模块
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});