React 代码库如何构建 umd 单文件?

1,053 阅读2分钟

参考

构建流程

以构建 umd 版本 react.development.js 为例:

React 源代码使用 rollup 进行构建。

yarn build react/index,react-dom/index --type=UMD

会运行 ./scripts/rollup/build.js 文件,调用异步方法 buildEverything(),该方法内部核心代码:

for (const [bundle, bundleType] of bundles) {
  await createBundle(bundle, bundleType);
}

批量调用 createBundle 方法,输出不同的文件:

通过命令行传的参数判断是否应该跳过某些类型的构建,本次运行只会构建 react、react-dom 包的 umd 版本。

shouldBundleDependencies: 判断是否需要绑定依赖项,umd 版本需要绑定 pureExternalModules: 如果为 true 则表示没有导入任何内容的外部依赖项不会产生副作用,例如修改全局变量等。该参数用于 treeshake。

构建 rollupConfig 对象,该对象包含一个 plugins 插件,使用方法参考插件开发。其中使用到自定义插件 getPlugins->sizes 方法用于记录本次构建生成的文件大小和 gzip 后的大小,该插件定义如下:

// 调用 sizes 函数返回一个插件对象
sizes({
  getSize: (size, gzip) => {
    const currentSizes = Stats.currentBuildResults.bundleSizes;
    const recordIndex = currentSizes.findIndex(
      record =>
        record.filename === filename && record.bundleType === bundleType
    );
    const index = recordIndex !== -1 ? recordIndex : currentSizes.length;
    currentSizes[index] = {
      filename,
      bundleType,
      packageName,
      size,
      gzip,
    };
  },
}),

// 插件定义
const gzip = require('gzip-size');

module.exports = function sizes(options) {
  return {
    name: 'scripts/rollup/plugins/sizes-plugin',
    generateBundle(outputOptions, bundle, isWrite) {
      Object.keys(bundle).forEach(id => {
        const chunk = bundle[id];
        if (chunk) {
          const size = Buffer.byteLength(chunk.code);
          const gzipSize = gzip.sync(chunk.code);
          options.getSize(size, gzipSize);
        }
      });
    },
  };
};

其中 generateBundle 为 rollup 暴露出来的 Output Generation Hooks。回调 getSize 函数后,会改变 Stats.currentBuildResults 对象,用于最终输出构建前后文件大小的比较的结果。

使用 rollupConfig 调用 rollup.rollup() 函数,并将结果写入输出目录下

const result = await rollup.rollup(rollupConfig);
await result.write(rollupOutputOptions);

createBundle 方法执行完毕。

最终使用 Stats.printResults() 输出构建结果,使用 cli-table 输出表格样式的构建前后文件对比图示。并将构建文件信息写入到 build/bundle-sizes.json 文件。

本系列文件开源在 read-source-code