Rollup 本身只负责核心的打包逻辑,诸如模块解析、语法转换、代码压缩、开发服务器等高级能力,几乎全部通过插件完成
一个最小可用的插件通常是一个“工厂函数”,返回包含若干钩子的对象:
export default function myPlugin(options = {}) {
return {
name: "my-plugin",
buildStart(inputOptions) {
console.log("build start", inputOptions.input)
},
generateBundle(outputOptions, bundle, isWrite) {
console.log("generate bundle", Object.keys(bundle), isWrite)
}
}
}常见字段:
name: 插件名称,必填,用于错误栈和调试输出options、buildStart、resolveId、load、transform、generateBundle 等使用时,只需要在 rollup.config 中把插件加入 plugins 数组:
import myPlugin from "./my-rollup-plugin.js"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "es",
sourcemap: true
},
plugins: [myPlugin()]
}下表列出构建阶段最常用的钩子及其用途(按典型调用顺序排列):
| 钩子名 | 调用时机 | 函数签名(简化) | 返回值 | 常见用途 |
|---|---|---|---|---|
options | 处理用户输入配置之前 | (inputOptions) | 新的或修改后的 inputOptions | 动态修改或补全配置 |
buildStart | 构建开始 | (inputOptions) | void | 初始化插件状态、打印信息 |
resolveId | 解析模块 ID 时 | (source, importer, options) | 字符串或对象,或 null | 自定义模块解析、别名、虚拟模块 |
load | 加载模块内容时 | (id) | 源码字符串或对象,或 null | 读取虚拟模块内容或特殊资源 |
transform | 每个模块加载完成后 | (code, id) | 新代码字符串或 { code, map } | 代码转换、宏展开、注入辅助函数 |
moduleParsed | AST 解析完成后 | (moduleInfo) | void | 基于 AST 的分析或收集信息 |
buildEnd | 构建完成(无论成功与否) | (error) | void | 释放资源、输出日志 |
watchChange | watch 模式下文件变更时 | (id, change) | void | 响应文件变更,更新内部缓存 |
输出阶段主要关注生成 bundle、重写代码片段和落盘逻辑:
| 钩子名 | 调用时机 | 函数签名(简化) | 返回值 | 常见用途 |
|---|---|---|---|---|
outputOptions | 处理每个输出配置之前 | (outputOptions) | 新的或修改后的 outputOptions | 动态修改输出目录、文件名、格式 |
renderStart | 开始渲染某次输出时 | (outputOptions, inputOptions) | void | 输出信息、准备辅助数据 |
renderChunk | 生成单个 chunk 代码时 | (code, chunk, options) | 新代码字符串或 { code, map } | 注入运行时代码、按 chunk 做优化 |
augmentChunkHash | 计算 chunk hash 时 | (chunkInfo) | 字符串 | 控制缓存策略、强制 hash 变更 |
generateBundle | 输出 bundle 元信息阶段 | (outputOptions, bundle, isWrite) | void 或修改 bundle | 生成额外文件、写出清单或分析报告 |
writeBundle | 所有文件写入磁盘后 | (outputOptions, bundle) | void | 输出日志、上传产物、清理临时文件 |
closeBundle | 整个构建流程完全结束后 | () | void | 关闭连接、释放全局资源 |
在钩子内部,this 指向插件上下文,可以调用一些工具方法:
this.warn(message):输出非致命警告this.error(message):抛出构建错误并终止流程this.emitFile(descriptor):声明资源文件或 chunk,例如 { type: "asset", name, source }this.getFileName(fileId):通过 emitFile 得到的 fileId 获取最终产物文件名this.addWatchFile(id):在 watch 模式下额外监听某个文件this.getModuleInfo(id):获取模块依赖信息、导入导出列表等这些 API 是实现复杂自定义插件(例如多入口控制、分析工具、资源管理)的基础
this.error / this.warn在自定义插件里,推荐使用上下文方法进行错误与警告输出,例如在构建开始阶段检查必要的环境变量:
export default function checkEnvPlugin() {
return {
name: "check-env",
buildStart() {
if (!process.env.NODE_ENV) {
this.warn("NODE_ENV is not set, defaulting to development")
}
}
}
}在 rollup.config.js 中,通过 plugins 数组配置插件:
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import json from "@rollup/plugin-json"
import { babel } from "@rollup/plugin-babel"
import typescript from "@rollup/plugin-typescript"
import { terser } from "@rollup/plugin-terser"
export default {
input: "src/index.ts",
output: [
{
file: "dist/index.cjs",
format: "cjs",
sourcemap: true
},
{
file: "dist/index.esm.js",
format: "es",
sourcemap: true
}
],
plugins: [
nodeResolve({
extensions: [".mjs", ".js", ".json", ".ts"]
}),
commonjs(),
json(),
typescript({
tsconfig: "./tsconfig.json"
}),
babel({
babelHelpers: "bundled",
extensions: [".js", ".ts"],
exclude: "node_modules/**"
}),
terser()
]
}此配置是一个完整可运行的示例,适合构建一个 TypeScript 编写的通用 JS 库
resolveId、load、transform)按 plugins 数组顺序依次执行renderChunk、generateBundle、writeBundle)同样按 plugins 数组顺序依次执行(而非相反顺序)watchChange 只在 --watch 模式下触发closeBundle 在所有输出结束后触发因此通常的插件排列顺序建议是:
@rollup/plugin-node-resolve、别名插件等@rollup/plugin-commonjs@rollup/plugin-babel、@rollup/plugin-typescript@rollup/plugin-terser、分析可视化插件等实际项目中通常会根据环境选择不同插件,例如开发环境需要 HMR,生产环境需要压缩:
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import { terser } from "@rollup/plugin-terser"
const isProd = process.env.NODE_ENV === "production"
export default {
input: "src/main.js",
output: {
file: isProd ? "dist/bundle.min.js" : "dist/bundle.js",
format: "iife",
sourcemap: !isProd
},
plugins: [nodeResolve(), commonjs(), isProd && terser()].filter(Boolean)
}本节对常用官方插件的 功能、常用配置项和使用场景 进行说明
@rollup/plugin-node-resolve@rollup/plugin-node-resolve 支持从 node_modules 中解析第三方依赖,识别 module、main 等字段
npm install --save-dev @rollup/plugin-node-resolve常用配置项:
extensions: 解析时尝试的文件后缀数组,默认 [".mjs", ".js", ".json", ".node"]。browser: 为浏览器环境选择 package.json 中的 browser 字段moduleDirectories: 额外的模块查找目录。示例:
import { nodeResolve } from "@rollup/plugin-node-resolve"
export default {
input: "src/index.js",
output: {
file: "dist/bundle.js",
format: "es"
},
plugins: [
nodeResolve({
browser: true,
extensions: [".js", ".jsx", ".mjs"]
})
]
}典型场景:打包浏览器端项目或希望消费第三方库的 ESM 构建产物
@rollup/plugin-alias@rollup/plugin-alias 用于为模块导入配置简洁的路径别名,避免层层 ../../../。
npm install --save-dev @rollup/plugin-alias常用配置项:
entries: 别名数组,形如 { find: '@', replacement: 'src' }。示例:
// rollup.config.js
import alias from "@rollup/plugin-alias"
import { nodeResolve } from "@rollup/plugin-node-resolve"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "es",
sourcemap: true
},
plugins: [
alias({
entries: [
{ find: "@", replacement: "src" } // 支持 import('@/utils/xxx')
]
}),
nodeResolve()
]
}典型场景:统一管理项目内长路径导入,配合 TypeScript paths 或 Webpack alias 等保持一致
@rollup/plugin-commonjs@rollup/plugin-commonjs 将 CommonJS 模块转换为 ES 模块,便于 Rollup 进行 Tree-shaking。
npm install --save-dev @rollup/plugin-commonjs常用配置项:
include: 指定需要转换的文件范围,支持通配符exclude: 指定不转换的文件ignoreTryCatch: 忽略某些包中的 require 调用示例:
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "iife",
name: "MyApp"
},
plugins: [
nodeResolve(),
commonjs({
include: "node_modules/**"
})
]
}典型场景:依赖大量 CommonJS 包(例如老版本的工具库、Node 生态库)时
@rollup/plugin-replace@rollup/plugin-replace 在构建时用指定字面量替换源码中的变量,常用于注入 process.env.NODE_ENV 或开关调试代码。
npm install --save-dev @rollup/plugin-replace使用时需要注意:替换值应为字符串字面量(通常通过 JSON.stringify 包装)。
示例:
// rollup.config.js
import replace from "@rollup/plugin-replace"
const isProd = process.env.NODE_ENV === "production"
export default {
input: "src/main.js",
output: {
file: isProd ? "dist/bundle.min.js" : "dist/bundle.js",
format: "iife",
name: "MyApp"
},
plugins: [
replace({
preventAssignment: true,
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development")
})
]
}典型场景:按环境注入开关、删除开发调试代码、控制日志输出等
@rollup/plugin-babel@rollup/plugin-babel 集成 Babel,将现代 JavaScript 转换为兼容旧环境的代码
npm install --save-dev @rollup/plugin-babel @babel/core @babel/preset-env关键配置:
babelHelpers: 指定辅助函数的注入策略,常用值为 "bundled"、"runtime"。extensions: 需要通过 Babel 处理的文件扩展名。exclude: 排除 node_modules 以提升性能。示例:
import { babel } from "@rollup/plugin-babel"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "iife",
name: "MyApp"
},
plugins: [
babel({
babelHelpers: "bundled",
extensions: [".js", ".jsx"],
exclude: "node_modules/**"
})
]
}搭配 .babelrc:
{
"presets": [["@babel/preset-env", { "modules": false }]]
}注意:"modules": false 可以避免 Babel 把 ES 模块转换成 CommonJS,从而保持 Rollup 的 Tree-shaking 效果
@rollup/plugin-typescript@rollup/plugin-typescript 直接编译 TypeScript 源码并交给 Rollup 继续处理
npm install --save-dev @rollup/plugin-typescript typescript常用配置项:
tsconfig: 指定 tsconfig.json 路径tsconfig.json 控制,例如 target、module示例:
import typescript from "@rollup/plugin-typescript"
export default {
input: "src/index.ts",
output: {
dir: "dist",
format: "es"
},
plugins: [
typescript({
tsconfig: "./tsconfig.json"
})
]
}搭配 tsconfig.json:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"declaration": true,
"outDir": "dist"
},
"include": ["src"]
}@rollup/plugin-json@rollup/plugin-json 将 .json 文件视为模块导入,允许 import data from "./data.json"
npm install --save-dev @rollup/plugin-json常用配置项:
preferConst: 使用 const 声明导出变量compact: 是否压缩生成代码示例:
import json from "@rollup/plugin-json"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "es"
},
plugins: [
json({
compact: true,
preferConst: true
})
]
}@rollup/plugin-terser@rollup/plugin-terser 使用 Terser 对生成的代码进行压缩与混淆,多用于生产构建
npm install --save-dev @rollup/plugin-terser示例:
import { terser } from "@rollup/plugin-terser"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.min.js",
format: "iife",
name: "MyApp"
},
plugins: [
terser({
compress: {
drop_console: true
}
})
]
}除了官方插件,社区还提供了大量插件以支持 CSS、图片、Vue/React 组件等多种场景。推荐的插件列表可以参考 awesome-rollup
rollup-plugin-postcss典型能力:
示例:
import postcss from "rollup-plugin-postcss"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "iife",
name: "MyApp"
},
plugins: [
postcss({
modules: true,
extract: "styles.css"
})
]
}rollup-plugin-vue注:rollup-plugin-vue 已多年未活跃维护,Vue 3 库打包也可考虑使用
@vitejs/plugin-vue(Vite 场景)或unplugin-vue。以下写法仍可运行。
示例(库型 Vue 组件打包):
import vue from "rollup-plugin-vue"
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
export default {
input: "src/index.js",
output: {
file: "dist/my-vue-lib.esm.js",
format: "es",
sourcemap: true
},
external: ["vue"],
plugins: [nodeResolve(), commonjs(), vue()]
}通常使用官方插件组合即可:
import { nodeResolve } from "@rollup/plugin-node-resolve"
import commonjs from "@rollup/plugin-commonjs"
import { babel } from "@rollup/plugin-babel"
export default {
input: "src/index.jsx",
output: {
file: "dist/my-react-lib.esm.js",
format: "es",
sourcemap: true
},
external: ["react", "react-dom"],
plugins: [
nodeResolve({
extensions: [".js", ".jsx"]
}),
commonjs(),
babel({
babelHelpers: "bundled",
extensions: [".js", ".jsx"],
exclude: "node_modules/**"
})
]
}transform 阶段做重复工作当现有插件无法满足需求时,可以编写自定义插件。下面通过一个简单示例展示完整开发流程
process.env.* 环境变量插件实现:
export default function envReplacePlugin(env = {}) {
const replacements = Object.entries(env).map(([key, value]) => ({
key: `process.env.${key}`,
value: JSON.stringify(value)
}))
return {
name: "env-replace",
transform(code) {
let transformed = code
for (const item of replacements) {
transformed = transformed.split(item.key).join(item.value)
}
return {
code: transformed,
map: null
}
}
}
}在配置中使用:
import envReplacePlugin from "./my-env-replace-plugin.js"
export default {
input: "src/main.js",
output: {
file: "dist/bundle.js",
format: "es"
},
plugins: [
envReplacePlugin({
NODE_ENV: "production",
API_BASE_URL: "https://api.example.com"
})
]
}示例,在构建前后打印信息和统计产物:
export default function buildReporter() {
let startTime = 0
return {
name: "build-reporter",
buildStart() {
startTime = Date.now()
console.log("Rollup build started")
},
generateBundle(outputOptions, bundle) {
const files = Object.keys(bundle)
console.log("Generated files:", files.length)
for (const file of files) {
const asset = bundle[file]
const size = Buffer.byteLength(asset.code || asset.source, "utf8")
console.log(file, size, "bytes")
}
},
closeBundle() {
const duration = Date.now() - startTime
console.log("Rollup build finished in", duration, "ms")
}
}
}该插件利用了 buildStart、generateBundle 和 closeBundle 三个钩子,覆盖了从开始构建到输出结束的完整生命周期