1. 为什么选择 Rollup 打包 Vue 项目
第一次接触 Rollup 是在为组件库寻找轻量化打包方案时。当时 webpack 的打包体积让我头疼——基础配置生成的 bundle 就超过 40KB,这对于需要被频繁加载的 UI 组件来说简直是灾难。经过对比测试,同样的 Vue 单文件组件用 Rollup 打包后,体积直接缩减了 62%,这让我开始认真研究这个"打包界的轻量级选手"。
Rollup 的核心优势在于其基于 ES Modules 的静态分析能力。与 webpack 不同,它不会在每个模块外包裹一层函数作用域,而是直接将所有 ES6+ 的 import/export 语句进行扁平化处理。这种设计特别适合 Vue 这类现代前端框架,因为:
- Vue 3 的 Composition API 本身就是基于 ES Modules 的代码组织方式
- 单文件组件(.vue)在编译后本身就是标准的 ES Module
- 第三方依赖如 vue-router、pinia 也都遵循 ESM 规范
实战经验:在打包包含 20+ 组件的库时,Rollup 产物体积比 webpack 平均小 35-45%,Tree-shaking 效果更彻底
2. 基础环境搭建
2.1 初始化项目结构
推荐使用以下目录结构(以组件库为例):
code复制my-vue-lib/
├── packages/ # 组件源码
│ ├── button/
│ │ ├── src/
│ │ │ └── Button.vue
│ │ └── index.js
├── rollup.config.js
└── package.json
关键配置项说明:
json复制{
"type": "module",
"scripts": {
"build": "rollup -c"
},
"devDependencies": {
"rollup": "^3.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-commonjs": "^24.0.0",
"@vue/compiler-sfc": "^3.3.0"
}
}
2.2 核心插件选型
-
@rollup/plugin-vue:处理 .vue 单文件组件
- 必须配合 @vue/compiler-sfc 使用
- 支持 Vue 3 的
<script setup>语法
-
rollup-plugin-postcss:处理组件内样式
- 自动提取
<style>块内容 - 支持 Sass/Less 预处理器(需额外安装)
- 自动提取
-
@rollup/plugin-terser:代码压缩
- 比 UglifyJS 对 ES6+ 支持更好
- 可配置压缩参数
典型配置示例:
javascript复制import vue from '@rollup/plugin-vue'
import postcss from 'rollup-plugin-postcss'
export default {
plugins: [
vue({
css: false // 禁用内置 CSS 处理
}),
postcss({
extract: true,
plugins: []
})
]
}
3. 高级打包策略
3.1 多格式输出配置
专业级库需要同时支持多种模块规范:
javascript复制export default {
output: [
{
file: 'dist/my-lib.esm.js',
format: 'esm',
sourcemap: true
},
{
file: 'dist/my-lib.cjs.js',
format: 'cjs',
exports: 'auto'
},
{
file: 'dist/my-lib.umd.js',
format: 'umd',
name: 'MyLib',
globals: {
vue: 'Vue'
}
}
]
}
3.2 外部依赖处理
避免将 Vue 打包进产物:
javascript复制export default {
external: ['vue', 'vue-router'],
output: {
globals: {
vue: 'Vue',
'vue-router': 'VueRouter'
}
}
}
踩坑记录:曾经忘记配置 external,导致打包后的组件库无法与宿主项目的 Vue 实例共享,引发重复注册警告
3.3 按需加载实现
通过多入口配置实现组件级按需加载:
javascript复制import glob from 'fast-glob'
export default {
input: Object.fromEntries(
glob.sync('packages/*/index.js').map(file => [
file.split('/')[1],
file
])
),
output: {
dir: 'dist',
format: 'esm',
preserveModules: true
}
}
4. 性能优化实战
4.1 Tree-shaking 深度优化
- 确保所有依赖提供 ESM 版本
- 在 package.json 中添加:
json复制{ "sideEffects": false, "module": "dist/my-lib.esm.js" } - 使用 @rollup/plugin-analyzer 可视化分析
4.2 代码分割策略
动态导入示例:
javascript复制// src/main.js
export const lazyLoadComponent = () => import('./HeavyComponent.vue')
配置代码分割:
javascript复制export default {
output: {
chunkFileNames: '[name]-[hash].js',
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
}
}
4.3 构建缓存方案
- 使用 rollup-plugin-cache 缓存 AST
- 持久化缓存配置:
javascript复制import cache from 'rollup-plugin-cache' export default { plugins: [ cache({ dir: 'node_modules/.cache/rollup' }) ] }
实测数据:在 50+ 组件项目中,启用缓存后二次构建时间从 12.3s 降至 3.8s
5. 常见问题排查
5.1 Vue 组件样式丢失
典型症状:
- 组件功能正常但样式不生效
- 生产环境样式错乱
解决方案:
- 检查 rollup-plugin-postcss 配置
javascript复制postcss({ extract: true, // 必须设置为 true minimize: true }) - 确保在 Vue 插件中禁用 CSS 处理
javascript复制vue({ css: false })
5.2 动态导入报错
错误信息:
code复制Uncaught (in promise) TypeError: Failed to fetch dynamically imported module
修复步骤:
- 检查输出格式是否为 esm
- 配置正确的 publicPath:
javascript复制output: { chunkFileNames: 'assets/[name]-[hash].js', assetFileNames: 'assets/[name]-[hash][extname]' }
5.3 第三方库兼容问题
典型场景:使用 CommonJS 模块的老库
解决方案:
- 安装 @rollup/plugin-commonjs
- 优先寻找 ESM 版本替代品
- 自定义转换规则:
javascript复制commonjs({ transformMixedEsModules: true, requireReturnsDefault: 'auto' })
6. 工程化进阶配置
6.1 自动化生成类型声明
使用 rollup-plugin-dts:
javascript复制import dts from 'rollup-plugin-dts'
export default {
input: 'src/index.ts',
output: {
file: 'dist/index.d.ts',
format: 'es'
},
plugins: [dts()]
}
6.2 集成测试环境
配置方案:
javascript复制import alias from '@rollup/plugin-alias'
export default {
plugins: [
alias({
entries: [
{ find: '@', replacement: path.resolve(__dirname, 'src') },
{ find: 'vue', replacement: path.resolve(__dirname, 'node_modules/vue/dist/vue.runtime.esm-bundler.js') }
]
})
]
}
6.3 多环境差异化打包
通过环境变量区分:
javascript复制const isProduction = process.env.NODE_ENV === 'production'
export default {
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
isProduction && terser()
]
}
7. 与 Vite 的协作模式
虽然 Vite 内部使用 Rollup,但在实际项目中它们可以这样配合:
-
开发阶段使用 Vite 的即时服务
javascript复制// vite.config.js import vue from '@vitejs/plugin-vue' export default { plugins: [vue()] } -
生产构建使用 Rollup 精细控制
javascript复制// rollup.config.js import { defineConfig } from 'vite' import rollupConfig from './rollup.config.js' export default defineConfig({ build: { rollupOptions: rollupConfig } })
性能对比数据:
- Vite 开发服务器冷启动:1.2s
- Rollup 生产构建(50组件):8.7s
- Webpack 同等构建:14.3s
8. 自定义插件开发
8.1 Vue SFC 转换插件示例
javascript复制export default function vueSfcPlugin() {
return {
name: 'vue-sfc-transform',
transform(code, id) {
if (!id.endsWith('.vue')) return
const { descriptor } = compiler.parse(code)
const script = compiler.compileScript(descriptor)
const template = compiler.compileTemplate({ source: descriptor.template.content })
return {
code: `
${script.content}
${template.code}
export default { render: ${template.code} }
`,
map: null
}
}
}
}
8.2 资源加载优化插件
javascript复制export function assetInlinePlugin(limit = 8192) {
return {
name: 'asset-inline',
transform(code, id) {
const [path, query] = id.split('?')
if (!/\.(png|jpg|svg)$/.test(path)) return
const file = fs.readFileSync(path)
if (file.length < limit) {
const mimetype = mime.getType(path)
const base64 = file.toString('base64')
return `export default "data:${mimetype};base64,${base64}"`
}
}
}
}
9. 监控与持续优化
9.1 构建指标分析
推荐工具组合:
- rollup-plugin-visualizer:生成 treemap 图表
- bundlesize:设置体积阈值
- speed-measure-webpack-plugin:对比 webpack 性能
配置示例:
javascript复制import visualizer from 'rollup-plugin-visualizer'
export default {
plugins: [
visualizer({
filename: 'bundle-analysis.html',
open: true
})
]
}
9.2 长期维护策略
- 版本锁定策略:
json复制{ "resolutions": { "@rollup/plugin-vue": "7.1.0" } } - 更新检查流程:
- 每月执行
npm outdated - 使用 npm-check-updates 进行安全更新
- 回归测试关键指标:
- 构建时间
- 输出体积
- Tree-shaking 效果
- 每月执行
10. 从 Webpack 迁移指南
10.1 配置对比表
| 功能点 | Webpack 配置 | Rollup 等效方案 |
|---|---|---|
| 入口文件 | entry | input |
| 输出目录 | output.path | output.dir |
| 代码分割 | splitChunks | manualChunks |
| 静态资源处理 | file-loader | rollup-plugin-url |
| 环境变量 | DefinePlugin | @rollup/plugin-replace |
10.2 渐进式迁移步骤
- 在新分支安装 Rollup 基础依赖
- 先迁移工具类/工具函数模块
- 逐步迁移简单组件
- 最后处理复杂组件和路由
- 并行运行两套构建系统对比结果
迁移实战:某后台系统从 webpack 4 迁移到 Rollup 后,构建时间从 2分18秒降至 47秒,首屏资源体积减少 41%