前言
UMD(Universal Module Definition)是一种通用的模块定义规范,旨在使同一个模块能够在不同的环境中使用,包括浏览器、Node.js 等。它采用一种灵活的方式,既支持类似 CommonJS 的模块加载方式,也支持类似 AMD 的异步加载方式,同时兼容浏览器全局变量的使用。
UMD 模块通常通过一定的判断逻辑来确定当前的模块加载环境,从而决定采用何种加载方式。这种灵活性使得开发者能够编写一次模块代码,然后在不同的环境中使用,无需修改代码。
本节对应的所有 demo 在此处
UMD 模块的基本结构
UMD 模块通常采用一种通用的结构,用于在不同的加载环境中判断和执行。以下是一个简单的 UMD 模块结构示例:
(function(root, factory){if(typeof define ==="function"&& define.amd){// AMDdefine(["dependency"], factory);}elseif(typeof define ==="function"&& define.cmd){// CMD 环境define(function(require, exports, module){
module.exports =factory(require("dependency"));});}elseif(typeof exports ==="object"){// Node.js/CommonJS
module.exports =factory(require("dependency"));}else{// Browser globals
root.YourModule =factory(root.Dependency);}})(typeof self !=="undefined"? self :this,function(Dependency){// 你的模块代码return YourModule;// 暴露模块});
此结构中,通过判断 define、exports 和全局对象,确定当前所处的环境,从而选择合适的加载方式。
React 中的 UMD 模块
React 是一个流行的 JavaScript 库,用于构建用户界面。React 库也支持 UMD 格式,使得它可以在浏览器和 Node.js 等环境中使用。
在浏览器中使用 React 的 UMD 模块的示例:
<!DOCTYPEhtml><html><head><metacharset="utf-8"/><title>React Demo template</title><scriptsrc="https://cdn.jsdelivr.net/npm/[email protected]/umd/react.development.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/[email protected]/umd/react-dom.development.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/@babel/[email protected]/babel.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/dayjs/dayjs.min.js"></script><scriptsrc="https://cdn.jsdelivr.net/npm/antd/dist/antd.min.js"></script></head><body><divid="root"></div><scripttype="text/babel">const{ render }= ReactDOM;// ReactDOM的UMD对象const{ useEffect }= React;const{ Button }= antd;// antd的UMD对象// AppfunctionApp(props:{name: string }){return(<div><h1>Hello,{props.name}!</h1><Button type="primary">click me</Button></div>);}// ----------------------------------------------------render(<App name="App"/>, document.getElementById("root"));</script></body></html>
注意上述的代码中使用了
type="text/babel"
,这是因为 React 使用了 JSX 语法,需要使用 Babel 进行转换。如果不使用 JSX 语法,可以将
type="text/babel"
去掉。
完整的 demo 请看 👉 在线效果预览, 查看示例代码请点击此处
回到 React, 其 UMD 模块基本结构如下:
(function(global, factory){typeof exports ==="object"&&typeof module !=="undefined"?factory(exports):typeof define ==="function"&& define.amd
?define(["exports"], factory):((global = global || self),factory((global.React ={})));// 我们可以从这里看出,React 的 UMD 模块实际上是将 React 挂载到全局对象上})(this,function(exports){"use strict";// React 导出的代码
exports.Component = Component;
exports.createElement = createElement;
exports.createContext = createContext;// ... (其他导出的React功能)});
手动实现一个 UMD 模块
假设你实现了一个 Hello 模块, 代码如下
functionsayHello(){
console.log("Hello!");}
第一步,准备一个 factory 函数,用于在不同的环境中执行,代码如下:
function(){functionsayHello(){
console.log('Hello!');}return{sayHello: sayHello
};}
第二步: 全局兼容
(function(root, factory){
console.log("-----global环境--------");
root.Hello =factory();})(this,function(){functionsayHello(){
console.log("Hello!");}return{sayHello: sayHello,};});
在这一步中,我们简单地将模块挂载到全局对象 this 上。这样,你可以在浏览器环境中通过 YourModule 来访问模块。
执行结果如下:
-----global 环境--------
Hello.js:6 Hello!
完整的 demo 请看 👉 在线效果预览, 查看示例代码请点击此处
第三步: CommonJS 兼容
(function(root, factory){if(typeof exports ==="object"&&typeof module !=="undefined"){
console.log("-----commonJS环境--------");
module.exports =factory();}else{
console.log("-----global环境--------");
root.Hello =factory();}})(this,function(){functionsayHello(){
console.log("Hello!");}return{sayHello: sayHello,};});
我们通过判断 module 和 module.exports 的存在,确定当前环境是否支持 CommonJS。如果是 CommonJS 环境,我们将模块导出,否则继续挂载到全局对象。
执行结果如下:
-----commonJS 环境--------
Hello!
查看示例代码请点击此处
第四步: AMD
(function(root, factory){if(typeof exports ==="object"&&typeof module !=="undefined"){
console.log("-----commonJS环境, 如nodeJS--------");
module.exports =factory();}elseif(typeof define ==="function"&& define.cmd){
console.log("-----CMD环境, 如seaJS--------");define([], factory);}elseif(typeof define ==="function"&& define.amd){
console.log("-----AMD环境, 如requireJS--------");define([], factory);}else{
console.log("-----global环境--------");
root.Hello =factory();}})(this,function(){functionsayHello(){
console.log("Hello!");}return{sayHello: sayHello,};});
在这一步中,我们通过判断 define 函数的存在,确定当前环境是否支持 AMD。如果是 AMD 环境,我们通过 define 函数来定义模块,否则继续挂载到全局对象。
输出结果如下:
-----AMD 环境, 如 requireJS--------
hello.js:17 Hello!
完整的 demo 请看 👉 在线效果预览, 查看示例代码请点击此处
第五步: CMD 支持
(function(root, factory){if(typeof exports ==="object"&&typeof module !=="undefined"){
console.log("-----commonJS环境, 如nodeJS--------");
module.exports =factory();}elseif(typeof define ==="function"&& define.cmd){
console.log("-----CMD环境, 如seaJS--------");define([], factory);}elseif(typeof define ==="function"&& define.amd){
console.log("-----AMD环境, 如requireJS--------");define([], factory);}else{
console.log("-----global环境--------");
root.Hello =factory();}})(this,function(){functionsayHello(){
console.log("Hello!");}return{sayHello: sayHello,};});
输出结果如下:
-----CMD 环境, 如 seaJS--------
Hello.js:17 Hello!
完整的 demo 请看 👉 在线效果预览, 查看示例代码请点击此处
本文首发于个人 Github前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正
版权归原作者 逍遥的编程笔记 所有, 如有侵权,请联系我们删除。