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

前言

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

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

本节对应的所有 demo 在此处

UMD 模块的基本结构

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

(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" />
    <title>React Demo template</title>
    <script src="https://cdn.jsdelivr.net/npm/react@17.0.2/umd/react.development.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/react-dom@17.0.2/umd/react-dom.development.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7.22.13/babel.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/dayjs/dayjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/antd/dist/antd.min.js"></script>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      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>
          </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 模块, 代码如下

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前端开发笔记,由于笔者能力有限,文章难免有疏漏之处,欢迎指正

  • 25
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript模块化指的是将代码分割成可重用的、独立的模块,以便提高代码的可维护性、可重用性和可扩展性。模块化前端开发中扮演着非常重要的角色。 CMD和AMD是两种常用的JavaScript模块化规范,它们都允许模块化编程,但它们的实现方式略有不同。 1. CMD(Common Module Definition) CMD是一种JavaScript模块化规范,它是由阿里前端开发玉伯提出的。CMD规范实现工具有Sea.js、RequireJS等。CMD规范的特点是延迟执行,即模块在require时才会执行,不会立即执行。其语法如下: ```javascript define(function(require, exports, module) { // 模块代码 }); ``` 2. AMD(Asynchronous Module Definition) AMD也是一种JavaScript模块化规范,它是由Dojo的前端开发者提出的。AMD规范实现工具有RequireJS等。AMD规范的特点是提前执行,即模块在define时就会执行,而不是等到require时才执行。其语法如下: ```javascript define(['module1', 'module2'], function(module1, module2) { // 模块代码 }); ``` 3. UMD(Universal Module Definition) UMD是一种通用模块定义规范,它可以在AMD和CommonJS规范之间进行切换,适用于多种JavaScript环境。其语法如下: ```javascript (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(['jquery'], factory); } else if (typeof exports === 'object' && typeof module === 'object') { // CommonJS module.exports = factory(require('jquery')); } else { // Browser globals root.returnExports = factory(root.jQuery); } }(this, function ($) { // 模块代码 })); ``` 以上三种模块化规范都有其各自的优缺点,选择适合自己的规范进行开发即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值