Rollup.js 入门

作者:刘专,日期:2017 年 12 月 13 日

Rollup 是一个 ES6 模块打包器,开发者 Rich Harris

Rich Harris 是纽约时报的图形编辑,既做新闻,也做开发。

Rollup 适合构建库文件,目前使用它的库有 vue.jsreact、D3、Three.js、Redux 等。

快速使用

使用 npm install --global rollup 全局安装,rollup --help 可以查看帮助文档。

以下命令假设你的入口文件是 main.js,并且输出文件是单一文件 bundle.js

# 对于浏览器,编译成 IIFE 函数
rollup main.js --o bundle.js --f iife

# 对于 Node.js,编译为 CommonJS 模块
rollup main.js --o bundle.js --f cjs

# 为了兼容浏览器和 Node.js,编译为 umd 格式
rollup main.js --o bundle.js -f umd --name "myBundle"

使用配置文件

在项目根目录下新建 rollup.config.js,写入内容:

// rollup.config.js
export default {
    input: 'src/main.js',
    output: {
        file: 'bundle.js',
        format: 'cjs'
    }
}

使用 --config-c 标志位,就能使用配置文件:

rollup -c

除了默认的 rollup.config.js,还能使用不同的配置文件:

rollup --config rollup.config.dev.js
rollup --config rollup.config.prod.js

使用插件

插件可在关键节点改变 Rollup 的默认行为,Rollup wiki 页面列举许多插件。

比如,rollup-plugin-json 允许 Rollup 导入 JSON 文件。

首先,安装它:

npm install --save-dev rollup-plugin-json

更新 main.js

import { version } from './package.json'

export default function() {
    console.log('version ' + version)
}

更新 rollup.config.js,使用 JSON 插件:

import json from 'rollup-plugin-json'

export default {
    input: 'main.js',
    output: {
        file: 'bundle.js',
        format: 'cjs'
    },
    plugins: [json()]
}

运行 npm run build 就能看到结果。

高级配置

Rollup 配置文件是一个标准 ES6 模块,需要导出一个默认值。

除了上面展示的简单配置,还可设定复杂配置参数。

比如,若入口文件不止一个,模块导出值须是一个数组;若想得到一个入口文件的多个版本,须将 output 设为数组:

export default [
    {
        input: 'main-a.js',
        output: {
            file: 'dist/bundle-a.js',
            format: 'cjs'
        }
    },
    {
        input: 'main-b.js',
        output: [
            {
                file: 'dist/bundle-b1.js',
                format: 'cjs'
            },
            {
                file: 'dist/bundle-b2.js',
                format: 'es'
            }
        ]
    }
]

配置参数若要异步生成,亦可:

// rollup.config.js
import fetch from 'node-fetch'
export default fetch('/some-remote-url')

再看一个 Promise :

export default Promise.all([
    fetch('get-config-1')
    fetch('get-config-2')
])

与其他武器整合

npm 包

Rollup 不知道如何读取 npm 包,不懂 node_modules 解析规则。

此时,我们需要 rollup-plugin-node-resolve 插件:

npm install --save-dev rollup-plugin-node-resolve

更新 rollup.config.js 以便引入它:

import resolve from 'rollup-plugin-node-resolve'

export default {
    // ...
    plugins: [resolve()]
}

目前大部分 npm 包都是 CommonJS 规范,若要被 Rollup 使用,须转换为 ES2015 模块。此时,可用 rollup-plugin-commonjs 插件。

注意,为避免其他插件篡改 CommonJS 模块,rollup-plugin-commonjs 须首先被调用。

Babel

同时使用 Babel 和 Rollup 最简单办法是引入 rollup-plugin-babelbabel-core 插件:

npm install --save-dev rollup-plugin-babel babel-core

然后更新配置文件:

// rollup.config.js
import resolve from 'rollup-plugin-node-resolve'
import babel from 'rollup-plugin-babel'

export default {
    // input & output ...
    plugins: [
        resolve(),
        babel({
            exclude: 'node_modules/**'
        })
    ]
}

Babel 须做如下配置才可工作,创建 src/.babelrc

{
    "presets": [
        [
            "env",
            { "modules": false }
        ]
    ],
    "plugins": ["external-helpers"]
}

此处有些异样须小心谨慎。首先,设定 "modules": false 选项,否则,Babel 会抢在 Rollup 之前将模块改为 CommonJS 。

第二,使用 external-helpers 插件。它仅在 bundle 头部引入一次 helpers 。否则,将在每个模块引入一次,浪费。

第三,.babelrc 置于 src/ 目录,而非项目根目录。可依不同目的(比如测试、开发、上线)创建不同参数。

最后,还需安装上述 env 预设值和 external-helpers 插件:

npm install --save-dev babel-preset-env babel-plugin-external-helpers

万事具备。只差 ES2015 代码:

import answer from 'the-answer'

export default () => {
    console.log(`the answer is ${answer}`)
}

Tree-shaking

通过摇树算法,可以把无关代码“抖掉”,减小文件体积。

兼容性

Rollup 可以通过 rollup-plugin-commonjs 插件引入 CommonJS 模块。

如果希望在 Node.js 或 webpack 中直接使用,可以把 rollup 的目标格式设定为 UMD 或 CommonJS ,然后在 package.jsonmain 属性指向编译后的版本。如果 package.json 中还包含 module 字段,rollup 和 webpack 2 等支持 ES6 的工具可以直接使用 ES6 版本的代码。

工作原理

首先,Rollup 使用 Acorn 解析器读取入口文件,分析后产生抽象语法树(AST)。通过 AST 就能得到很多信息,比如模块的依赖和导出变量。

如果发现依赖模块,就加载模块、读其内容、拆其 Token、产生AST。递归深入,直到汇集所有模块。

每个步骤都是可插拔的,可向其中增加自定义操作,比如读取 node_module 文件夹,将 ES2015 编译至 ES5 等。

与其他打包器有什么不同?

首先,Rollup 打的包精炼,没有多余包装。

其次,Rollup 会剔除没用的代码,让包尽可能小。

REF