rollup技术揭秘系列一准备篇(可能是全网最系统性的rollup源码分析文章)

1,132 阅读5分钟

一、rollup 介绍

rollup 是干什么的?

Rollup 是一个 JavaScript 模块打包器,可以将小块代码打包成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用新的标准化格式(ES module),这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而让你的项目中不会存在任何多余的代码(treeshake 技术)。

rollup 可以输出哪些格式的文件

rollup 打包支持输出 amd , cjs , esm , iife , umd , system 六种格式的文件。

学习 rollup 的前置条件

rollup 使用 typescript 编写,因此要求阅读源码的人具有一定的 typescript 基础知识储备。如果对 typescript 不熟的同学建议提前查阅 官方文档 以便你更好的阅读 rollup 源码。

本系列源码分析基于哪个版本

本系列源码分析基于 rollup-3.2.3 版本

二、rollup源码构建过程

构建脚本

打开项目的根目录下的 package.json 文件找到 scripts:

"scripts": {
    "build": "rollup --config rollup.config.ts --configPlugin typescript",
    //...
}

这里暂时只对 build 命令作介绍,其他的就不一一举例了:

首先使用 rollup 本身作为打包器。 --config rollup.config.ts 的意思是使用 rollup.config.ts 作为配置文件。--configPlugin typescript 的意思是使用 typescript 来编写配置文件。rollup 的配置文件是可选的,但是使用配置文件的作用很强大,而且很方便,因此更推荐使用。配置文件是一个 ES6 模块,它对外暴露一个对象,这个对象包含了一些 rollup 需要的一些选项。通常,我们把这个配置文件叫做 rollup.config.js,它通常位于项目的根目录。

注意:如果只带上了 --config 且没有指定 config 文件那么 rollup 会去找默认的 config 文件。默认的配置文件为 rollup.config.js

当我们在命令行执行 npm run build,实际上执行的就是 rollup --config rollup.config.ts --configPlugin typescript 这段代码。

构建过程

打开 rollup.config.ts 文件我们可以看到这个文件中默认导出了一个函数:

export default async function (
  command: Record<string, unknown>
): Promise<RollupOptions | RollupOptions[]> {
  const { collectLicenses, writeLicense } = getLicenseHandler(
    fileURLToPath(new URL('.', import.meta.url))
  );
  //打包成cjs
  const commonJSBuild: RollupOptions = {
    // 'fsevents' is a dependency of 'chokidar' that cannot be bundled as it contains binary code
    external: ['fsevents'],
    input: {
      'loadConfigFile.js': 'cli/run/loadConfigFile.ts',
      'rollup.js': 'src/node-entry.ts'
    },
    onwarn,
    output: {
      banner: getBanner,
      chunkFileNames: 'shared/[name].js',
      dir: 'dist',
      entryFileNames: '[name]',
      exports: 'named',
      externalLiveBindings: false,
      format: 'cjs',
      freeze: false,
      generatedCode: 'es2015',
      interop: 'default',
      manualChunks: { rollup: ['src/node-entry.ts'] },
      sourcemap: true
    },
    plugins: [
      ...nodePlugins,
      addCliEntry(),
      esmDynamicImport(),
      !command.configTest && collectLicenses(),
      !command.configTest && copyTypes('rollup.d.ts')
    ],
    strictDeprecations: true,
    treeshake
  };

  if (command.configTest) {
    return commonJSBuild;
  }
  //打包成 es 格式
  const esmBuild: RollupOptions = {
    ...commonJSBuild,
    input: { 'rollup.js': 'src/node-entry.ts' },
    output: {
      ...commonJSBuild.output,
      dir: 'dist/es',
      format: 'es',
      minifyInternalExports: false,
      sourcemap: false
    },
    plugins: [...nodePlugins, emitModulePackageFile(), collectLicenses(), writeLicense()]
  };

  const { collectLicenses: collectLicensesBrowser, writeLicense: writeLicenseBrowser } =
    getLicenseHandler(fileURLToPath(new URL('browser', import.meta.url)));

  //打包成 umd 和 es 两种格式
  const browserBuilds: RollupOptions = {
    input: 'src/browser-entry.ts',
    onwarn,
    output: [
      {
        banner: getBanner,
        file: 'browser/dist/rollup.browser.js',
        format: 'umd',
        name: 'rollup',
        plugins: [copyTypes('rollup.browser.d.ts')],
        sourcemap: true
      },
      {
        banner: getBanner,
        file: 'browser/dist/es/rollup.browser.js',
        format: 'es',
        plugins: [emitModulePackageFile()]
      }
    ],
    plugins: [
      replaceBrowserModules(),
      alias(moduleAliases),
      nodeResolve({ browser: true }),
      json(),
      commonjs(),
      typescript(),
      terser({ module: true, output: { comments: 'some' } }),
      collectLicensesBrowser(),
      writeLicenseBrowser(),
      cleanBeforeWrite('browser/dist')
    ],
    strictDeprecations: true,
    treeshake
  };

  return [commonJSBuild, esmBuild, browserBuilds];
}

这个默认的函数接收唯一的 command 参数,它实际上就是由 scripts 脚本里面传入的参数组成的一个对象。当我们使用 npm run build 的时候这个参数对象大概长这样:

command:  {
  _: [],
  config: 'rollup.config.ts',
  c: 'rollup.config.ts',
  configPlugin: 'typescript'
}

在这里我先告诉同学们一个看源码的技巧:当我们想要大概了解一个函数的功能的时候我们可以只看函数的输出(也就是 return 了啥)。那我们可以看到最后函数这句:

//...
return [commonJSBuild, esmBuild, browserBuilds];

再结合上下文可以得知:

  • commonJSBuild 其实就是返回了 cjs 的一个 RollupOptions
  • esmBuild 其实就是 es 风格的一个 RollupOptions
  • browserBuilds 返回了包含了 umd 和 es 风格的 RollupOptions。

并且当我们使用 npm run build:cjs 时候,这个命令带了一个 --configTest 参数。上面的函数就会返回一个 commonJSBuild 的 RollupOptions :

//...
if (command.configTest) {
  return commonJSBuild;
}
//...

总结

通过这一节的分析,我们可以了解到 rollup 源码的构建打包过程,也知道了使用不同命令参数来打包生成它们对应格式的最终文件。现在我们先执行下 npm run build ,然后通过一个例子开始我们本次的 rollup 技术揭秘之旅吧!

rollup 揭秘相关文章