跳到主要内容

UMD

2023年12月18日
柏拉文
越努力,越幸运

一、认识


UMDUniversal Module Definition 规范是一种旨在兼容多种模块化环境的模块定义标准。它提供了一种方式兼容各种模块系统,包括 AMDCommonJS 和全局变量模式。UMD 规范允许模块在不同的环境中使用,使得同一个模块可以被 AMDCommonJS 和全局变量加载。UMD 规范使得模块可以无缝集成到不同的项目和工具链中,无需针对特定的模块加载系统进行调整。

UMD 支持多种模块环境:

  • AMD(Asynchronous Module Definition):模块加载器,如 RequireJSUMD 模块通过检查是否存在 define 函数来决定是否使用 AMD 规范。

  • CommonJS:如 Node.jsUMD 模块通过检查是否存在 module.exports 来决定是否使用 CommonJS 规范。

  • 全局环境(浏览器):在浏览器环境中,UMD 模块会将模块内容挂载到 window 对象上,以便通过全局变量访问。

UMD 模块的各个部分:

  • IIFE(Immediately Invoked Function Expression)UMD 模块使用 IIFE 包裹模块代码,以确保模块在被加载时立即执行,并避免污染全局命名空间。IIFE 是一个自执行的函数表达式,它定义了模块的作用域。

    (function (root, factory) {
    // UMD 模块的实现
    }(this, function () {
    // 模块定义
    }));
  • AMD 支持UMD 模块会检查 define 是否为一个函数并且具有 amd 属性。如果是,则使用 AMD 规范来定义模块。

    if (typeof define === 'function' && define.amd) {
    define(['dependency'], factory);
    }
  • CommonJS 支持UMD 模块会检查 module 是否为对象并且 module.exports 存在。如果是,则使用 CommonJS 规范来导出模块。

    else if (typeof module === 'object' && module.exports) {
    module.exports = factory(require('dependency'));
    }
  • 全局环境支持: 如果以上条件都不满足,UMD 模块将模块内容挂载到全局对象(如 window),以便在浏览器环境中直接通过全局变量访问。

    else {
    root.myModule = factory(root.dependency);
    }

二、语法


定义模块

(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// 1. 支持 AMD: 检查 `define` 是否为一个函数,并且具有 `amd` 属性。如果是,则使用 `AMD` 规范来定义模块。
define(['dependency1'], factory);
} else if (typeof module === 'object' && module.exports) {
// 2. 支持 CommonJS: 检查 `module` 是否为对象并且 `module.exports` 存在。如果是,则使用 `CommonJS` 规范来导出模块。
module.exports = factory(require('dependency1'));
} else {
// 3. 支持 Browser: 如果以上条件都不满足,模块将被定义为全局变量,供浏览器中的全局作用域访问。 root 就是 window
root.myModule = factory(root.dependency1);
}
}(this, function (dep1) {
// Define your module here
var module = {};

module.greet = function(name) {
return 'Hello, ' + name;
};

return module;
}));

使用模块

// AMD
require(['myModule'], function(myModule) {
console.log(myModule.greet('World')); // Hello, World
});

// CommonJS
var myModule = require('./myModule');
console.log(myModule.greet('World')); // Hello, World

// Global
console.log(myModule.greet('World')); // Hello, World

三、场景


3.1 Vite

3.2 Node

// umdModule.js

(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(["dependency"], factory);
} else if (typeof module === "object" && module.exports) {
// CommonJS
module.exports = factory(require("dependency"));
} else {
// Browser (root is window)
root.umdModule = factory(root.dependency);
}
})(this, function (dependency) {
return {
foo: function () {
console.log("foo 函数");
},
};
});
// app.js
const umdModule = require("./umdModule.js");
umdModule.foo();

3.3 Rollup

3.4 Browser

// umdModule.js
(function (root, factory) {
if (typeof define === "function" && define.amd) {
// AMD
define(["dependency"], factory);
} else if (typeof module === "object" && module.exports) {
// CommonJS
module.exports = factory(require("dependency"));
} else {
// Browser (root is window)
root.umdModule = factory(root.dependency);
}
})(this, function (dependency) {
return {
foo: function () {
console.log("foo 函数");
},
};
});
<script src="./umdModule.js"></script>
<script>
const { umdModule } = window;
umdModule.foo();
</script>

3.5 Webpack

一、为什么将产物打包成 UMD 格式?

  • 兼容性UMD 格式兼容多种模块系统,包括 AMDCommonJS 和全局变量。这使得模块能够在不同的环境和项目中无缝使用,而无需重新打包或修改代码。

  • 通用性UMD 模块可以在浏览器、Node.jsRequireJS 等环境中使用。适用于需要支持多种加载器和环境的公共库或工具。

  • 灵活性UMD 格式允许在没有模块加载器的情况下直接在浏览器中使用模块(通过全局变量)。这对那些不使用模块加载器的旧系统或简单项目非常有用。

  • 广泛的应用场景:许多开源库和工具使用 UMD 格式,以确保它们可以在各种项目中被广泛接受和使用。

二、什么场景下需要将产物打包成 UMD 格式呢?

  • 公共库和工具:如开源库、第三方工具和框架,通常需要在不同的环境中被使用(如浏览器和 Node.js)。UMD 格式保证了它们的兼容性和灵活性。

  • 企业内部工具:在企业内部,可能存在不同的技术栈和模块加载方式。UMD 格式的库可以在这些不同环境中都能正常工作。

  • 无需安装模块加载器的环境:对于某些简单的项目或静态网页,可能不使用模块加载器。UMD 格式的模块可以直接作为全局变量使用,无需额外配置。

三、Webpack 通过 librarylibraryTargetglobalObject 将打包产物配置为 UMD 格式, 配置如下:

const path = require('path');

module.exports = {
entry: './src/index.js', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录
filename: 'bundle.js', // 输出文件名
library: 'MyLibrary', // 模块名(在 UMD 模式下,模块将以 `MyLibrary` 名称暴露)
libraryTarget: 'umd', // 输出模块格式为 UMD
globalObject: 'this' // 确保在 Node.js 和浏览器环境中都能正常工作
},
mode: 'production' // 或 'development'
};
  • library: 指定模块暴露的全局变量名。在 UMD 模式下,库将暴露为全局变量。如果库在浏览器中被加载,它将以此名称挂载到全局对象上。

  • libraryTarget: 设置模块的输出格式。设置为 umd 表示生成 UMD 格式的产物。

  • globalObject: 指定全局对象的名称。设置为 this 可以确保在不同的 JavaScript 环境中(如 Node.js 和浏览器)都能正确工作。