快速开始
初始化
1 2 3 4
| # 初始化项目 pnpm init -y # 安装 rollup pnpm install rollup
|
根目录下新建 rollup 的配置文件 rollup.config.js
1 2 3 4 5 6 7 8
| export default { input: 'src/main.js', output: { file: 'dist/bundle.js', format: 'cjs' } };
|
新建 src/main.js src/util.js
1 2 3 4 5 6 7
| import { add } from "./util";
console.log(add(1, 2));
export const add = (a, b) => a + b; export const multi = (a, b) => a * b;
|
package.json 增加 build (部分代码省略),这里如果是 windows 环境,建议先安装 rimraf,将 rm -rf 替换成 rimraf ,我们在 build 时需要先将 dist 目录删掉重新生成
1 2 3 4 5
| { "scripts": { "build": "rm -rf dist && rollup -c" } }
|
安装 rimraf 命令
1
| pnpm install rimraf --save-dev
|
现在执行 pnpm run build 就能看到在 dist 目录下生成 bundle.js
1 2
| const add = (a, b) => a + b; console.log(add(1, 2));
|
打包后的文件可以看到代码已经放到一起了,并且我们在 util 中声明的方法 multi 并没又出现在打包后的文件中,因为 rollup 自带 Tree-Shaking 功能。
Tree-Shaking 也可以翻译为”摇树“,把一些不需要的东西抖落下来,本质就是在打包时,将一些没有用到的代码(也称 Dead Code)消除掉。
配置文件
详细文档:选项大列表
多产物配置
通过 format 指定不同的输出格式,可以同时打包出不同格式的产物,这里的 format 支持 amd、cjs、es、iife、umd、system
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| export default { input: ['src/main.js', 'src/util.js'], output: [ { dir: "dist/es", format: "es", }, { dir: "dist/cjs", format: "cjs", }, { dir: "dist/amd", format: "amd", }, ] };
|
类型:string | string [] | { [entryName: string]: string }
如果有多个入口就需要用 数组/对象 的形式来定义了,需要注意的是,使用多入口时,产物需要定义 dir 指定输出目录
1 2 3 4 5 6 7 8
| export default { input: ['src/main.js', 'src/util.js'], output: { dir: "dist/es", format: "es", }, };
|
如果想要定制每个入口的配置,可以通过导出数组的方式,来定义多个配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const mainBuild = { input: ['src/main.js', 'src/util.js'], output: [ { dir: "dist/cjs", format: "cjs", }, ] }
const utilBuild = { input: ['src/util.js'], output: [ { dir: "dist/amd", format: "amd", }, ] }
export default [mainBuild, utilBuild];
|
插件
插件有两种定义方式,第一种定义在 output 中,定义在这里的插件需要使用了 Output 阶段的钩子,第二种就是定义在 output 同级。
1 2 3 4 5 6 7 8 9 10 11
| const mainBuild = { input: ['src/main.js', 'src/util.js'], output: [ { dir: "dist/cjs", format: "cjs", plugins: [terser()] }, ], plugins: [resolve(), commonjs()] }
|
我们可以通过引入插件的方式来解决一些问题,例如使用到第三方依赖使用的是 CommonJs 格式的产物,rollup 有把 ESM 打包成 CommonJs 的能力,但他却不能反向处理,这时候就需要引入插件来处理了。
JavaScript API
当我们需要扩展 Rollup 本身,或者自己定制打包过程,需要使用 JavaScript API , rollup 提供了两个 API rollup.rollup 和 rollup.watch
rollup.rollup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const rollup = require('rollup');
const outputOptions = [ { dir: "dist/cjs", format: "cjs", }, { }, ] async function build() { const bundle = await rollup.rollup({ input: ['./src/main.js','./src/util.js'], }); try { for (let output of outputOptions) { const result = await bundle.generate(output); await bundle.write(output); } } catch { } await bundle.close(); } build();
|
首先通过 rollup.rollup 存储各个模块之前的内容依赖关系,完成模块图构建和 tree-shaking,返回一个 bundle 对象
然后遍历输出配置项,分别执行 bundle.generate 输出产物
最后通过 bundle.write 把产物写进磁盘
rollup.watch
当检测到磁盘单个模块发生变化,进行重构 bundle,我们来新建一个 watcher.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const rollup = require('rollup');
const watchOptions = { input: ['./src/main.js','./src/util.js'], output: [ { dir: "dist/cjs", format: "cjs", }, { dir: "dist/amd", format: "amd", }, ], watch: { exclude: ["node_modules/**"], include: ["src/**"], }, }; const watcher = rollup.watch(watchOptions);
watcher.on('event', event => { console.log(`watch 到了 ${event.code}`) });
|
watch 的配置项和我们写的输入输出配置项相同,只是多了一个 watch 项
通过 rollup.watch 返回监听对象,通过 watcher.on 进行监听。
event.code:
START — 监听器正在启动(重启)
BUNDLE_START — 构建单个 bundle
BUNDLE_END — 完成 bundle 构建
END — 完成所有bundle构建
ERROR — 构建时遇到错误
插件机制
构建流程
在前文的 JavaScript API 的 rollup.rollup 自定义构建中,能大概看出 rollup 经历了 Input => Build => Output,另外在真正的打包是在 Output 阶段,也就是执行 generate / write 的时候
Hooks
Build Hook:在build阶段执行的 hook,主要进行了代码转换,AST解析,模块依赖解析
Output Generation Hook:进行代码打包
执行方式
- Async:异步钩子函数
- Sync:同步钩子函数
- Parallel:并行钩子函数,底层使用 Promise.all 实现,处理没有依赖关系的插件
- Sequential:串行钩子函数,处理有依赖关系的插件
- First:当多个插件实现了这个 Hook ,会依次执行,直到返回值不为 null/undefined