UMD 是 JavaScript 模块的通用模块定义模式。这些模块能够在任何地方工作,无论是在客户端、服务器还是其他地方。
UMD 模式通常试图提供与当今最流行的脚本加载器(例如 RequireJS 等)的兼容性。 在许多情况下,它使用 AMD 作为基础,并添加了特殊的外壳来处理 CommonJS 兼容性。
所以首先要了解 AMD.
AMD
AMD 代表异步模块定义(Asynchronous Module Definition). 它是 CommonJS (CJS) 规范的替代品。
API 指定了一种定义模块的机制,以便可以异步加载模块及其依赖项。 这特别适用于模块的同步加载会导致性能、可用性、调试和跨域访问问题的浏览器环境。
AMD 库公开了一个全局定义函数,其 footprint 为:
define(modulename?,[dependencyA?, dependencyB?, …], function (objectA, objectB, …) {
...
var myExportedObj = function() { … }
return myExportedObj;
});
CommonJS
CommonJS 是一个项目,其目标是为浏览器之外的 JavaScript 指定一个生态系统(例如,在服务器上或本地桌面应用程序)。
服务器端 JavaScript 已经存在很长时间了,并且可能提供一些与其他语言相比独特而有趣的优势,因为客户端和服务器都使用相同的语言。
不幸的是,服务器端 JavaScript 非常分散。访问文件的脚本在 rhino 和 V8 上未经修改就无法使用。 Spidermonkey 和 JavaScriptCore 不能以相同的方式加载附加模块。 JavaScript Web 框架与其解释器密切相关,并且经常被迫创建一堆 Python、Ruby 和 Java 程序员认为理所当然的 API。
该项目的目标是创建一个标准库,最终允许 Web 开发人员在任意数量的 Web 框架和工具中进行选择,并在最适合其应用程序的平台上运行该代码。
例如, foo.js 在同一目录中加载模块 circle.js。
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
circle.js:
const PI = Math.PI;
exports.area = function (r) {
return PI * r * r;
};
exports.circumference = function (r) {
return 2 * PI * r;
};
模块 circle.js 已经导出了函数 area(..) 和 circle(..)。 要将函数和对象添加到模块的根目录,您可以将它们添加到特殊的导出对象。
模块本地的变量将是私有的,就像模块被包装在一个函数中一样。 在这个例子中,变量 PI 是 circle.js 私有的。
如果您希望模块导出的根是一个函数(例如构造函数),或者如果您想在一次分配中导出一个完整的对象而不是一次构建一个属性,请将其分配给 module.exports 而不是 exports.
例子:
const square = require('./square.js');
var mySquare = square(2);
console.log(`The area of my square is ${mySquare.area()}`);
square.js 的实现:
// Assigning to exports will not modify module, must use module.exports
module.exports = function (width) {
return {
area: function () {
return (width * width);
}
};
}
两个输入参数 global 和 factory 分别传入了 this 和 function(exports)...
要导出的函数 log 的实现体,只是一个简单的 console.log.
上图这个 require 其实是 Node.js 内部实现:
当前 module 是 local.js 即 node 命令启动的 module,期望加载的 是 log.js
从 module 的 paths 数组里就能看出 Node.js 解析 module 的路径:从当前 module 所在的文件夹出发,一直回溯到 c 盘根目录下的 node_modules
log.js 的 paths:
load 之前先 compile:
第 1157 行的代码位置,变量 content 的内容就是 log.js 文件的内容
compile 的逻辑就是执行 log.js 里的代码:
在这个上下文里,factory 指向的就是下图绿色高亮的函数:
define 为 undefined,所以进入 else 分支:
此处把 log 函数写入到 exports 对象的 log 属性里:
然后在我们的应用代码里,就可以使用导出的 log 函数了: