概念
- **Rollup 是一个 ES 模块打包器(bundler)**,擅长把多个 ESModule 打包成一个或多个 bundle,目标常是库(library)或前端应用的生产构建。
核心理念是 基于 ES Module 的静态分析:Rollup 能更精准做 tree-shaking(删除未使用导出),产出的 bundle 更小、更"纯净"。
Rollup 的打包结果更适合做库发布(输出 ESM/CJS/UMD),但在前端应用上(尤其配合 plugin)也非常强大(vite 打包器)。
Rollup 与其它 bundler 的定位对比
- Webpack:功能齐全、插件生态丰富、面向应用(复杂的 code-splitting、loader )。
- Rollup:更注重静态 ESM 分析和生成更小的库包,tree-shaking 更精准。用于库开发或作为生产打包(如 Vite 的 rollup 阶段)。
- esbuild / swc:超快的编译/转译工具,但打包能力、插件生态不如 Rollup 全面(可结合使用:esbuild 做转译,Rollup 做打包/优化 => vite)。
Rollup 工作流程
- **输入(entry)**:指定入口文件。
- 解析依赖 :基于 ES Module 静态分析
import/export,并通过插件解析 node_modules(@rollup/plugin-node-resolve)、 CommonJS(@rollup/plugin-commonjs)等。 - 转换:插件可对模块做转换(TS -> JS、JSX -> JS、替换环境变量等)。
- 合并与静态分析:Rollup 构建模块图,决定哪些导出被引用(用于 Tree-shaking)。
- 生成 chunks / assets:按配置输出 JS chunk、CSS / images 等静态资产。
- 压缩 / 产出 sourcemap:可用 terser 等做最终压缩。
常用核心配置项(rollup.config.js)
JavaScript
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js', // 入口
external: ['react'], // 不打包的外部依赖(库通常会标记 peerDependencies)
plugins: [
resolve(), // 解析 node_modules 中的包
commonjs(), // 转换 CommonJS -> ESM
// 其他插件:babel/typescript/esbuild/postcss 等
terser(), // 压缩(只在 prod)
],
output: [
{
file: 'dist/index.esm.js',
format: 'es', // ESM
sourcemap: true,
},
{
file: 'dist/index.cjs.js',
format: 'cjs', // CommonJS
sourcemap: true,
}
]
};
输出格式(output.format)说明
es/esm:ES Module,现代打包器或 Node(>=支持 ESM)使用。推荐用于库的模块化输出。cjs:CommonJS,用于 Node 或老式打包消费者。umd:通用模块定义(UMD),适合直接在浏览器通过<script>使用(通常需要name+globals)。iife:立即调用函数表达式,单文件浏览器脚本(不依赖模块加载器)。amd/system:很少用,分别为 AMD 和 SystemJS 格式。
如果做库,一般输出 es + cjs(并可额外输出 umd 做浏览器兼容)。
external 与 globals
external:告诉 Rollup 哪些模块应视为外部依赖,不会被打包进 bundle(通常对 library 很重要)。例:external: ['react', 'react-dom']。output.globals:当 output.format 为umd/iife时,需要指定外部包在浏览器下的全局变量名,例如{ react: 'React' }。
Tree-shaking(摇树优化)
- Rollup 基于 ESM 的静态结构判断哪些导出可删;相比基于 AST 的后处理工具,Rollup 的精度很高。
- 重要配置:
treeshake: true | { moduleSideEffects: boolean | string[], propertyReadSideEffects: boolean }moduleSideEffects对应 package.json 的sideEffects字段:正确声明能让 Rollup 更激进地移除无用模块。
- 建议 :在
package.json中维护sideEffects,对于不会产生副作用的文件或包标注为 false(或列出那些有副作用的文件)。
Code-Splitting(代码分割)
- Rollup 支持多入口和动态
import()以生成多个 chunk。 - 常用配置:
input可以是数组或对象:input: { main: 'src/main.js', admin: 'src/admin.js' },会生成多个入口 chunk。output.manualChunks:手动控制 chunk 划分(把大型依赖拆出 vendor chunk 等)。
示例 manualChunks:
JavaScript
output: {
dir: 'dist',
format: 'esm',
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor';
}
}
}
插件生态(常用插件)
基本解析/转换:
@rollup/plugin-node-resolve:解析 node_modules,支持browser,extensions等选项。@rollup/plugin-commonjs:把 CommonJS 模块转为 ESM(一定要放在 resolve 之后)。@rollup/plugin-json:允许import data from './data.json'。
编译/转译:
@rollup/plugin-babel:Babel 转译(配合 preset-env)。@rollup/plugin-typescript:官方的 TS 支持(但常用rollup-plugin-typescript2,因为更成熟并支持 declaration 输出)。rollup-plugin-esbuild/@rollup/plugin-esbuild:使用 esbuild 做超快转译/TS -> JS(推荐用于提高构建速度)。
其他常用:
rollup-plugin-terser:压缩 JS。@rollup/plugin-replace:替换变量(例如process.env.NODE_ENV)。@rollup/plugin-alias:路径别名。rollup-plugin-postcss:处理 CSS(支持 CSS Modules、extract)。rollup-plugin-url:把小图片/字体转 base64,或复制为 assets。rollup-plugin-visualizer:生成 bundle 可视化报告(helpful)。rollup-plugin-serve/rollup-plugin-livereload:开发时的本地服务(轻量)。
这样排序更好理解 :通常为 replace → alias → resolve → commonjs → esbuild/babel/ts → postcss → terser。
尤其
resolve要在commonjs之前,commonjs需要转换解析到的包。
Vite 兴起
Vite 把「开发时不打包 、直接利用浏览器原生 ESM + 用 esbuild 做依赖预处理」和「生产构建使用 Rollup 做高质量打包」结合起来,既实现了极快的 dev 启动与 HMR,又保留了 Rollup 的生产打包能力与插件生态。(vitejs)
- 开发模式(dev server)是"按模块服务"
- Vite 在 dev 模式下**不把应用打包成一个大包(对比 webpack)**,而是把每个源码文件按 ESM 模块原样通过开发服务器直接发给浏览器,浏览器负责模块解析、按需加载。这样冷启动无需预构建整个项目,极大缩短启动时间并保持 HMR 快速。
- 依赖预打包(Dependency pre-bundling)用 esbuild
- 第一次启动时,Vite 会用 esbuild 将
node_modules中的依赖预打包为能被浏览器高效加载的 ESM 格式(放到.vite/deps目录),解决了 CommonJS、包内部不能直接以原始形式按原生 ESM 加载的问题,同时提升加载/解析速度。该步骤只是 dev-only 优化。
- 第一次启动时,Vite 会用 esbuild 将
- HMR(Hot Module Replacement)基于原生 ESM + websocket
- Vite 利用浏览器的 ESM 单模块热替换能力,只替换实际变更的模块并广播更新,避免全页刷新;框架层(比如 React/Vue)会处理局部状态的保持。Vite 提供
import.meta.hotAPI 供框架或用户控制 HMR 行为。
- Vite 利用浏览器的 ESM 单模块热替换能力,只替换实际变更的模块并广播更新,避免全页刷新;框架层(比如 React/Vue)会处理局部状态的保持。Vite 提供
- 生产构建使用 Rollup
- 上面说的都是开发环境,Vite 的
build阶段使用 Rollup 作为打包器(利用 Rollup 精准的 tree-shaking、code-splitting、和插件能力),并结合 esbuild 做快速的转译/压缩选项(或通过 terser 等 cn.vite.dev/config/buil...
- 上面说的都是开发环境,Vite 的
- 插件系统基于 Rollup 插件接口扩展
- Vite 的插件接口是 Rollup 插件的超集,很多 Rollup 插件可以复用,但不是所有 Hook 在 dev(无 bundling)场景下都适用,所以存在兼容性考虑(有
enforce: 'pre'|'post'来调整调用顺序,插件作者一般考虑,大多数无需我们开发者考虑)。
- Vite 的插件接口是 Rollup 插件的超集,很多 Rollup 插件可以复用,但不是所有 Hook 在 dev(无 bundling)场景下都适用,所以存在兼容性考虑(有
Vite 核心配置(vite.config.ts)
JavaScript
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';
import path from 'path';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import viteCompression from 'vite-plugin-compression'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import viteSvgLoader from 'vite-svg-loader';
import legacy from '@vitejs/plugin-legacy';
import { visualizer } from 'rollup-plugin-visualizer';
// 可以传递一个函数(需要额外参数的情况下),也可以直接传递一个对象
export default defineConfig(({ command, mode }) => {
const isDev = command === 'serve';
const isProd = command === 'build';
return {
root: process.cwd(),
base: '/', // 发布时根据需求改为 CDN 地址或相对路径
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'~': path.resolve(__dirname, 'src'),
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
plugins: [ // 控制预打包哪些依赖(有问题的 CJS 包需要手动 include/exclude)
// Vue3 SFC 支持
vue(),
// Vue3 JSX 支持
vueJsx(),
// SVG 文件直接当 Vue 组件使用(适合小图标)
viteSvgLoader(),
// svg-sprite(按需合并 svg,适合图标库)
createSvgIconsPlugin({
// 制定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
symbolId: 'icon-[dir]-[name]',
svgoOptions: isProd, // 生产时进行 svgo 优化
}),
// 压缩
viteCompression({
algorithm: 'gzip',
ext: '.gz',
threshold: 10240 // 10kB 以上才压缩
}),
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 10240
})
// 自动按需导入 Vue Composition API / Vue Router / Pinia 等
AutoImport({
imports: [
'vue',
'vue-router',
'pinia',
{
// 你可以在这里自己添加包的自动导入
axios: [
['default', 'axios']
]
}
],
dts: 'src/auto-imports.d.ts',
eslintrc: {
enabled: true, // 生成 .eslintrc-auto-import.json(可选)
filepath: './.eslintrc-auto-import.json',
globalsPropValue: true
}
}),
// 自动注册组件(按需加载 UI 组件库,例如 Element Plus)
Components({
dirs: ['src/components'],
extensions: ['vue', 'md'],
dts: 'src/components.d.ts',
resolvers: [
// 例:Element Plus 自动按需引入
ElementPlusResolver()
]
}),
// legacy 支持(如果需要支持旧浏览器)
legacy({
targets: ['defaults', 'not IE 11'] // 根据项目兼容性调整
}),
// 在生产构建时生成 bundle 分析报告(可选)
isProd && visualizer({
filename: './dist/stats.html',
open: false,
gzipSize: true
})
].filter(Boolean),
// 开发服务器配置
server: {
host: true, // 外网可访问(容器/VM 开发时有用)
port: 5173,
open: false,
strictPort: false,
hmr: {
overlay: true
},
proxy: {
// 示例:开发代理转发 /api 到后端
'/api': {
target: 'http://127.0.0.1:7001',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
secure: false,
ws: true
}
},
fs: {
// 如果是 monorepo,需要允许访问工作区外的文件夹
allow: [path.resolve(__dirname, '.')],
}
},
// 依赖预构建,控制预打包哪些依赖(有问题的 CJS 包需要手动 include/exclude)
optimizeDeps: {
// 生产预构建会使用 esbuild,把 node_modules 中的包转为 ESM,提高 dev 性能
include: [
'vue',
'vue-router',
'pinia',
'axios',
'lodash-es'
],
// 如果某些包有问题,可排除,强制让 Rollup 在 build 时处理
exclude: [
// 'some-cjs-only-package'
],
esbuildOptions: {
// 如果你需要注入 polyfill 或 target
target: 'es2018',
}
},
// 全局定义、构建选项
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
'process.env': {}
},
// CSS 相关配置(预处理器、模块化等)
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
},
modules: {
// CSS Modules 命名规则
generateScopedName: isProd ? '[hash:base64:8]' : '[name]__[local]__[hash:base64:5]'
},
postcss: {
// 可配置 autoprefixer / postcss-preset-env 等
}
},
// 构建(生产)相关,底层使用 Rollup
build: {
target: 'es2018',
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false,
minify: 'esbuild', // esbuild(默认值) 更快;如果想压缩得更小可改为 'terser'
cssCodeSplit: true,
rollupOptions: {
// external 可用于库模式,应用一般不用 external
// external: ['vue'],
output: {
// 手动分包:把 node_modules 全部算作 vendor
manualChunks(id) {
if (id.includes('node_modules')) {
// 可以按 lib 名拆分 vendor(例如 element-plus 单独 chunk)
if (id.includes('element-plus')) return 'vendor_elementplus';
if (id.includes('vue')) return 'vendor_vue';
return 'vendor';
}
},
entryFileNames: 'assets/js/[name]-[hash].js',
chunkFileNames: 'assets/js/chunks/[name]-[hash].js',
assetFileNames: (assetInfo) => {
// 图片文件
if (/\.(png|jpe?g|svg|gif|tiff|bmp|ico|webp)$/i.test(assetInfo.names[0] || '')) {
return `assets/images/[name]-[hash][extname]`
}
// 字体文件
if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.names[0] || '')) {
return `assets/fonts/[name]-[hash][extname]`
}
// CSS 文件
if (/\.css$/i.test(assetInfo.names[0] || '')) {
return `assets/css/[name]-[hash][extname]`
}
// 其他资源文件
return `assets/[name]-[hash][extname]`
},
}
},
// 生产构建的并行度控制、chunk 大小限制等
brotliSize: true,
// chunkSizeWarningLimit: 1500,
},
// SSR / library 等高级模式可以在这里配置
// ssr: { noExternal: ['some-esm-only-package'] },
// 测试(如果使用 vitest)
test: {
globals: true,
environment: 'jsdom'
}
};
});
```t resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js', // 入口
external: ['react'], // 不打包的外部依赖(库通常会标记 peerDependencies)
plugins: [
resolve(), // 解析 node_modules 中的包
commonjs(), // 转换 CommonJS -> ESM
Other plugins: babel/typescript/esbuild/postcss, etc.
terser(),t resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js', // 入口
external: ['react'], // 不打包的外部依赖(库通常会标记 peerDependencies)
plugins: [
resolve(), // 解析 node_modules 中的包
commonjs(), // 转换 CommonJS -> ESM
Other plugins: babel/typescript/esbuild/postcss, etc.
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/index.js', // 入口
external: ['react'], // 不打包的外部依赖(库通常会标记 peerDependencies)
plugins: [
resolve(), // 解析 node_modules 中的包
commonjs(), // 转换 CommonJS -> ESM
Other plugins: babel/typescript/esbuild/postcss, etc.
terser(),