使用 Rollup 构建你的 Library

使用 Rollup 构建你的 Library

Rollup, 和 Webpack, Parcel 都是模块打包工具(module bundler tool), 但是侧重点不同, 我们要聊的 Rollup 更加适合用于构建 Library, 而 Webpack, Precel 更加适合开发 Application.

希望通过本篇文章, 对大家构建 Library 有一定的工程上的启发.

什么是 Rollup

简单而言, Rollup 是一个模块打包工具, 可以将我们按照 ESM (ES2015 Module) 规范编写的源码构建输出如下格式:

  • IIFE: 自执行函数, 可通过 <script> 标签加载
  • AMD: 浏览器端的模块规范, 可通过 RequireJS 可加载
  • CommonJS: Node 默认的模块规范, 可通过 Webpack 加载
  • UMD: 兼容 IIFE, AMD, CJS 三种模块规范
  • ESM: ES2015 Module 规范, 可用 Webpack, Rollup 加载

大多数的 Library 也是选择使用 Rollup 构建, 比如: React, Vue, Angular, D3, Moment, Redux…

借助于 Rollup 的插件体系, 我们也可以处理 css, images, font 等资源, 但是 Rollup 不支持代码拆分(Code Splitting)和运行时态加载(Dynamic Import) 特性, 所以较少的应用于 Application 开发.


为什么选择 Rollup

  • Tree Shaking: 自动移除未使用的代码, 输出更小的文件
  • Scope Hoisting: 所有模块构建在一个函数内, 执行效率更高
  • Config 文件支持通过 ESM 模块格式书写
  • 可以一次输出多种格式:
    • 不用的模块规范: IIFE, AMD, CJS, UMD, ESM
    • Development 与 production 版本: .js, .min.js
  • 文档精简

使用教程

安装教程就不列举了, 为大家提供一个 starter 模板: rollup-starter-kit. 主要说下开发一个 Library 我们需要哪些基础插件以及他们的配置.

基础插件

Rollup 配置

构建命令我们通常还会使用 cross-env 区分 NODE_ENV, npm scripts 是这样:

{
    "clean": "rimraf dist",
    "start": "yarn run clean && cross-env NODE_ENV=development rollup -w -c scripts/rollup.config.dev.js",
    "build": "yarn run clean && cross-env NODE_ENV=production rollup -c scripts/rollup.config.prod.js",
}


Rollup 的配置文件建议拆分为多份, 比如:

  • rollup.base.js
  • rollup.dev.js
  • rollup.prod.js

rollup.base.js 中, 我们维护通用的配置:

import alias from 'rollup-plugin-alias';
import eslint from 'rollup-plugin-eslint';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel from 'rollup-plugin-babel';
import replace from 'rollup-plugin-replace';

export default {
  input: 'src/main.js',
  plugins: [
    alias({
      resolve: ['.js']
    }),
    replace({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
    }),
    resolve(),
    commonjs({
      // non-CommonJS modules will be ignored, but you can also
      // specifically include/exclude files
      include: 'node_modules/**'
    }),
    eslint({
      include: ['src/**/*.js']
    }),
    babel({
      runtimeHelpers: true,
      exclude: 'node_modules/**' // only transpile our source code
    })
  ]
}

开发环境配置

由于是开发 Library, 建议大家编写单元测试, 示例工程我们使用 Jest 作为测试框架. 开发过程中还可通过rollup-plugin-serve 提供静态资源访问:

plugins: [
    ...baseConfig.plugins,
    serve({
      port: 8080,
      contentBase: ['']
    })
  ]

生产环境的配置

构建生产版本时候, 我们一般期待结果有如下特性:

  • 去除开发环境调试信息
  • 一个 entry 文件, 多个 output 版本
  • 包含文件头注释, 可以是版本, 作者或开源协议声明
  • 文本混淆与压缩


对应于我们的配置文件如下:

import filesize from 'rollup-plugin-filesize';
import uglify from 'rollup-plugin-uglify';
import { minify } from 'uglify-es';

import baseConfig from './rollup.config.base';
import { name, version, author } from '../package.json';

// banner
const banner =
  `${'/*!\n' + ' * '}${name}.js v${version}\n` +
  ` * (c) 2018-${new Date().getFullYear()} ${author}\n` +
  ` * Released under the MIT License.\n` +
  ` */`;

// 支持输出 []
export default [
  // .js, .cjs.js, .esm.js
  {
    ...baseConfig,
    output: [
      // umd development version with sourcemap
      {
        file: `dist/${name}.js`,
        format: 'umd',
        name,
        banner,
        sourcemap: true
      },
      // cjs and esm version
      {
        file: `dist/${name}.cjs.js`,
        format: 'cjs',
        banner
      },
      // cjs and esm version
      {
        file: `dist/${name}.esm.js`,
        format: 'es',
        banner
      }
    ],
    plugins: [...baseConfig.plugins, filesize()]
  },
  // .min.js
  {
    ...baseConfig,
    output: [
      // umd with compress version
      {
        file: `dist/${name}.min.js`,
        format: 'umd',
        name,
        banner
      }
    ],
    plugins: [
      ...baseConfig.plugins,
      uglify(
        {
          compress: {
            drop_console: true
          }
        },
        minify
      ),
      filesize()
    ]
  }
];

Npm 包发布的一些建议

  • 通过 standard-version 工具管理发布的 tag 和 CHANGELOG.md
  • 包的名称带上命名空间: @xx/xx
  • 提供接口声明文件: xx.d.ts, 可通过 package.json 中 typings 指定
  • package.json 中 modules 指定 ESM 模块, webpack, rollup 会优先获取 ESM 模块
  • package.json 中 main 指定 CJS 模块

写在最后

文章主要讲解一些配置和实践规范, 具体效果可参见示例代码: rollup-starter-kit, 欢迎大家提 Issue 交流.

编辑于 2018-04-14 12:47