前端模块化-UMD规范及其实现

逍遥的编程笔记 2024-10-02 13:33:01 阅读 67

前言

UMD(Universal Module Definition)是一种通用的模块定义规范,旨在使同一个模块能够在不同的环境中使用,包括浏览器、Node.js 等。它采用一种灵活的方式,既支持类似 CommonJS 的模块加载方式,也支持类似 AMD 的异步加载方式,同时兼容浏览器全局变量的使用。

UMD 模块通常通过一定的判断逻辑来确定当前的模块加载环境,从而决定采用何种加载方式。这种灵活性使得开发者能够编写一次模块代码,然后在不同的环境中使用,无需修改代码。

本节对应的所有 demo 在此处

UMD 模块的基本结构

UMD 模块通常采用一种通用的结构,用于在不同的加载环境中判断和执行。以下是一个简单的 UMD 模块结构示例:

<code>(function (root, factory) { -- -->

if (typeof define === "function" && define.amd) {

// AMD

define(["dependency"], factory);

} else if (typeof define === "function" && define.cmd) {

// CMD 环境

define(function (require, exports, module) {

module.exports = factory(require("dependency"));

});

} else if (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 模块的示例:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />code>

<title>React Demo template</title>

<script src="https://cdn.jsdelivr.net/npm/react@17.0.2/umd/react.development.min.js"></script>code>

<script src="https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.development.min.js"></script>code>

<script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.22.13/babel.min.js"></script>code>

<script src="https://cdn.jsdelivr.net/npm/dayjs/dayjs.min.js"></script>code>

<script src="https://cdn.jsdelivr.net/npm/antd/dist/antd.min.js"></script>code>

</head>

<body>

<div id="root"></div>code>

<script type="text/babel">code>

const { -- --> render } = ReactDOM; // ReactDOM的UMD对象

const { useEffect } = React;

const { Button } = antd; // antd的UMD对象

// App

function App(props: { name: string }) {

return (

<div>

<h1>Hello, { props.name}!</h1>

<Button type="primary">click me</Button>code>

</div>

);

}

// ----------------------------------------------------

render(<App name="App" />, document.getElementById("root"));code>

</script>

</body>

</html>

注意上述的代码中使用了 type="text/babel"code>,这是因为 React 使用了 JSX 语法,需要使用 Babel 进行转换。如果不使用 JSX 语法,可以将 type="text/babel"code> 去掉。

完整的 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 模块, 代码如下

function sayHello() {

console.log("Hello!");

}

第一步,准备一个 factory 函数,用于在不同的环境中执行,代码如下:

function () {

function sayHello() {

console.log('Hello!');

}

return {

sayHello: sayHello

};

}

第二步: 全局兼容

(function (root, factory) {

console.log("-----global环境--------");

root.Hello = factory();

})(this, function () {

function sayHello() {

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 () {

function sayHello() {

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();

} else if (typeof define === "function" && define.cmd) {

console.log("-----CMD环境, 如seaJS--------");

define([], factory);

} else if (typeof define === "function" && define.amd) {

console.log("-----AMD环境, 如requireJS--------");

define([], factory);

} else {

console.log("-----global环境--------");

root.Hello = factory();

}

})(this, function () {

function sayHello() {

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();

} else if (typeof define === "function" && define.cmd) {

console.log("-----CMD环境, 如seaJS--------");

define([], factory);

} else if (typeof define === "function" && define.amd) {

console.log("-----AMD环境, 如requireJS--------");

define([], factory);

} else {

console.log("-----global环境--------");

root.Hello = factory();

}

})(this, function () {

function sayHello() {

console.log("Hello!");

}

return {

sayHello: sayHello,

};

});

输出结果如下:

-----CMD 环境, 如 seaJS--------

Hello.js:17 Hello!

完整的 demo 请看 👉 在线效果预览, 查看示例代码请点击此处

本文首发于个人 Github前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。