Appearance
Rollup 介绍
什么是 Rollup
Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用 ES Module 标准,而不是之前的特殊解决方案(如 CommonJS 和 AMD)。
Rollup 的核心特点
ES Module 优先
- 原生支持 ES Module
- 静态分析能力
- 更高效的 Tree Shaking
Tree Shaking
- 自动移除未使用的代码
- 基于静态分析
- 生成更小的包
输出格式多样
- 支持多种输出格式
- IIFE、AMD、CJS、UMD、ESM
- 适合不同使用场景
配置简洁
- 配置文件简单
- API 清晰
- 易于理解和使用
Rollup 的工作原理
构建流程
入口文件
↓
解析 AST
↓
分析依赖关系
↓
构建依赖图
↓
Tree Shaking
↓
代码生成
↓
输出文件静态分析
Rollup 利用 ES Module 的静态特性:
javascript
// 静态导入 - 编译时确定
import { foo } from './module';
// 动态导入 - 运行时确定(Rollup 也支持)
const module = await import('./module');Tree Shaking 原理
javascript
// utils.js
export function used() {
console.log('used');
}
export function unused() {
console.log('unused');
}
// main.js
import { used } from './utils';
used();
// 打包后 - unused 函数被移除
function used() {
console.log('used');
}
used();作用域提升
Rollup 将所有模块合并到一个函数作用域中:
javascript
// 打包前
// moduleA.js
export const a = 1;
// moduleB.js
export const b = 2;
// main.js
import { a } from './moduleA';
import { b } from './moduleB';
console.log(a, b);
// 打包后
(function() {
const a = 1;
const b = 2;
console.log(a, b);
})();Rollup 的配置
基础配置
javascript
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
}
};多格式输出
javascript
export default {
input: 'src/index.js',
output: [
{
file: 'dist/bundle.cjs.js',
format: 'cjs'
},
{
file: 'dist/bundle.esm.js',
format: 'esm'
},
{
file: 'dist/bundle.umd.js',
format: 'umd',
name: 'MyLibrary'
}
]
};使用插件
javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm'
},
plugins: [
resolve(),
commonjs(),
babel({ babelHelpers: 'bundled' }),
terser()
]
};代码分割
javascript
export default {
input: ['src/index.js', 'src/secondary.js'],
output: {
dir: 'dist',
format: 'esm'
}
};动态导入
javascript
export default {
input: 'src/index.js',
output: {
dir: 'dist',
format: 'esm',
chunkFileNames: 'chunks/[name]-[hash].js'
}
};输出格式详解
ES Module (esm)
javascript
// 输出格式
export const foo = 'bar';
export function baz() {}CommonJS (cjs)
javascript
// 输出格式
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const foo = 'bar';
exports.foo = foo;UMD (Universal Module Definition)
javascript
// 输出格式
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MyLibrary = {}));
}(this, (function(exports) {
'use strict';
const foo = 'bar';
exports.foo = foo;
})));IIFE (Immediately Invoked Function Expression)
javascript
// 输出格式
var MyLibrary = (function() {
'use strict';
const foo = 'bar';
return { foo: foo };
})();AMD
javascript
// 输出格式
define(['exports'], function(exports) {
'use strict';
const foo = 'bar';
exports.foo = foo;
});Rollup 的优点
高效的 Tree Shaking
- 基于静态分析
- 自动移除死代码
- 更小的输出体积
干净的输出
- 代码可读性高
- 没有额外的运行时代码
- 适合库开发
多格式支持
- 支持所有主流模块格式
- 一次配置多格式输出
- 灵活的使用场景
配置简单
- 配置文件简洁
- 概念清晰
- 学习成本低
插件机制
- 灵活的插件系统
- 丰富的插件生态
- 易于扩展
Rollup 的缺点
功能相对简单
- 不如 Webpack 功能全面
- 缺少一些开发时功能
- 需要插件支持更多功能
开发体验
- 没有内置开发服务器
- HMR 需要额外配置
- 不适合复杂应用开发
资源处理
- 需要插件处理 CSS、图片等
- 不如 Webpack 方便
- 配置相对繁琐
生态规模
- 插件数量少于 Webpack
- 社区相对较小
- 某些功能支持不完善
常用插件
官方插件
| 插件 | 用途 |
|---|---|
| @rollup/plugin-node-resolve | 解析 node_modules 中的模块 |
| @rollup/plugin-commonjs | 转换 CommonJS 模块 |
| @rollup/plugin-babel | Babel 编译 |
| @rollup/plugin-json | 导入 JSON 文件 |
| @rollup/plugin-url | 导入文件为 URL |
| @rollup/plugin-virtual | 虚拟模块 |
社区插件
| 插件 | 用途 |
|---|---|
| rollup-plugin-terser | 代码压缩 |
| rollup-plugin-typescript2 | TypeScript 支持 |
| rollup-plugin-postcss | CSS 处理 |
| rollup-plugin-livereload | 热重载 |
| rollup-plugin-serve | 开发服务器 |
| rollup-plugin-filesize | 显示文件大小 |
插件开发
插件结构
javascript
export default function myPlugin(options = {}) {
return {
name: 'my-plugin',
buildStart(options) {
console.log('构建开始');
},
resolveId(source, importer) {
if (source === 'virtual-module') {
return source;
}
return null;
},
load(id) {
if (id === 'virtual-module') {
return 'export default "This is virtual"';
}
return null;
},
transform(code, id) {
if (id.endsWith('.custom')) {
return {
code: `export default ${JSON.stringify(code)}`,
map: { mappings: '' }
};
}
return null;
},
buildEnd(error) {
if (error) {
console.error('构建错误:', error);
}
}
};
}插件钩子
| 钩子 | 类型 | 用途 |
|---|---|---|
| options | 同步 | 修改配置 |
| buildStart | 异步 | 构建开始 |
| resolveId | 同步/异步 | 解析模块 ID |
| load | 同步/异步 | 加载模块 |
| transform | 同步/异步 | 转换代码 |
| buildEnd | 异步 | 构建结束 |
| renderStart | 异步 | 生成阶段开始 |
| renderChunk | 同步/异步 | 处理每个 chunk |
| generateBundle | 异步 | 生成完成 |
| writeBundle | 异步 | 写入完成 |
高级配置
外部依赖
javascript
export default {
input: 'src/index.js',
external: ['react', 'react-dom', 'lodash'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
lodash: '_'
}
}
};条件导出
javascript
export default {
input: 'src/index.js',
output: {
exports: 'named' // 'auto' | 'default' | 'named' | 'none'
}
};Source Map
javascript
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
sourcemap: true,
sourcemapFile: 'dist/bundle.js.map'
}
};保留模块结构
javascript
export default {
input: 'src/index.js',
preserveModules: true,
output: {
dir: 'dist',
format: 'esm'
}
};Rollup 的应用场景
库开发
- npm 包开发
- UI 组件库
- 工具函数库
- 插件开发
简单应用
- 小型 Web 应用
- 静态网站
- 简单单页应用
Vite 生产构建
- Vite 底层使用 Rollup
- 生产环境打包
命令行工具
bash
# 基本使用
rollup src/index.js --file dist/bundle.js --format esm
# 使用配置文件
rollup -c
# 指定配置文件
rollup -c rollup.config.js
# 监听模式
rollup -c -w
# 指定输入输出
rollup src/index.js -o dist/bundle.js -f cjs
# 生成 source map
rollup src/index.js -o dist/bundle.js -f esm -m与 Webpack 的对比
| 特性 | Rollup | Webpack |
|---|---|---|
| Tree Shaking | 原生支持,效果好 | 支持,效果稍差 |
| 输出代码 | 干净、可读 | 有运行时代码 |
| 配置复杂度 | 简单 | 复杂 |
| 开发服务器 | 需要插件 | 内置 |
| HMR | 需要插件 | 内置 |
| 代码分割 | 支持 | 强大 |
| 适用场景 | 库开发 | 应用开发 |
| 生态 | 较小 | 庞大 |
最佳实践
库开发配置
javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import { terser } from 'rollup-plugin-terser';
const pkg = require('./package.json');
export default [
{
input: 'src/index.ts',
output: [
{ file: pkg.main, format: 'cjs' },
{ file: pkg.module, format: 'esm' }
],
external: [...Object.keys(pkg.dependencies || {})],
plugins: [
resolve(),
commonjs(),
typescript(),
terser()
]
}
];开发环境配置
javascript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import serve from 'rollup-plugin-serve';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'iife',
sourcemap: true
},
plugins: [
resolve(),
commonjs(),
serve({
contentBase: 'dist',
port: 3000
}),
livereload()
]
};