构建 umd 产物

umd 全称为 Universal Module Definition,这种格式的 JS 文件可以运行在多个运行环境:

  • 浏览器环境:基于 AMD 规范进行模块加载
  • Node.js 环境:基于 CommonJS 进行模块加载
  • 其他情况:将模块挂载在全局对象上。

因此我们可以通过下面的方式,将项目的构建产物指定为 umd 产物:

export default defineConfig({
  buildConfig: {
    format: 'umd',
  },
});

umd 产物的第三方依赖处理

「如何处理第三方依赖」 章节中,我们知道可以通过 autoExternalsexternals API 来控制项目是否对第三方依赖打包。 因此在构建 umd 产物的过程中,我们也可以这样使用:

示例

  • 如果项目依赖了 react
package.json
{
  "dependencies": {
    "react": "^17"
  }
}
  • modern.config.ts 配置:
modern.config.ts
export default defineConfig({
  buildConfig: {
    format: 'umd',
    autoExternal: false,
    externals: ['react'],
  },
});
  • 当源码中使用了 react 依赖:
src/index.ts
import React from 'react';
console.info(React);
  • 此时产物中不会将 react 代码打包到产物中:
dist/index.js
(function (global, factory) {
  if (typeof module === 'object' && typeof module.exports === 'object')
    factory(exports, require('react'));
  else if (typeof define === 'function' && define.amd)
    define(['exports', 'react'], factory);
  else if (
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self)
  )
    factory((global.index = {}), global.react);
})(this, function (exports, _react) {
  'use strict';
  Object.defineProperty(exports, '__esModule', {
    value: true,
  });
  _react = /*#__PURE__*/ _interopRequireDefault(_react);
  function _interopRequireDefault(obj) {
    return obj && obj.__esModule
      ? obj
      : {
          default: obj,
        };
  }
  console.info(_react.default);
});

通过上面的例子我们知道,当使用 autoExternalexternals API 后:

  • 在 Node.js 环境下,可以通过 require('react') 获取 react 依赖。
  • 在 浏览器环境下,可以通过 global.react 获取 react 依赖。

三方依赖的全局变量名称

然而在浏览器环境下,获取第三方依赖的时候,全局变量名称不一定与依赖名称完全相同,此时就要使用 buildConfig.umdGlobals API。

还是使用之前的例子,当 react 依赖以 windows.React 或者 global.React 全局变量的形式存在于浏览器环境下,那么此时:

  • modern.config.ts 配置:
export default defineConfig({
  buildConfig: {
    format: 'umd',
    umdGlobals: {
      react: 'React',
    },
  },
});
  • 当源码中使用了 react 依赖:
src/index.ts
import React from 'react';
console.info(React);
  • 此时我们会看到这样的产物代码:
dist/index.js
(function (global, factory) {
  if (typeof module === 'object' && typeof module.exports === 'object')
    factory();
  else if (typeof define === 'function' && define.amd) define([], factory);
  else if (
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self)
  )
    factory();
})(this, function () {
  // ...
  // libuild:globals:react
  var require_react = __commonJS({
    'libuild:globals:react'(exports, module1) {
      module1.exports = Function('return this')()['React'];
    },
  });
  // src/index.ts
  var import_react = __toESM(require_react());
  console.info(import_react.default);
});

此时项目就可以运行在浏览器中,并使用存在于全局对象上的 React 变量了。

更改项目的全局变量名称

当我们将下面的代码打包成 umd 产物并运行在浏览器的时候,我们可以通过 window.index 来使用该模块。

./src/index.ts
export default () => {
  console.info('hello world');
};

默认情况下,以源码文件名称作为该模块在浏览器里全局变量的名称。对于上面的例子,其产物内容如下:

./dist/index.js
(function (global, factory) {
  if (typeof module === 'object' && typeof module.exports === 'object')
    factory(exports);
  else if (typeof define === 'function' && define.amd)
    define(['exports'], factory);
  else if (
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self)
  )
    factory((global.index = {}));
})(this, function (exports) {
  //...
});

如果需要修改它,则需要使用 buildConfig.umdModuleName API。

当使用该 API 后:

export default defineConfig({
  buildConfig: {
    format: 'umd',
    umdModuleName: 'myLib',
  },
});

此时构建产物的内容为:

./dist/index.js
(function (global, factory) {
  if (typeof module === 'object' && typeof module.exports === 'object')
    factory(exports);
  else if (typeof define === 'function' && define.amd)
    define(['exports'], factory);
  else if (
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self)
  )
    factory((global.myLib = {}));
})(this, function (exports) {
  //...
});