Rollup打包配置踩坑记那些年我遇到的常见问题及解决方案
优化前:卡得不行
最近重构一个老项目的打包流程,用了Rollup来替换之前的Webpack。本来想着用Rollup的轻量级特性能让构建速度快一点,结果发现打包出来的文件居然比之前还要大,构建时间也慢得要命。开发环境热更新有时候要等个5-6秒才能看到效果,真是一言难尽。
更离谱的是,生产环境下首屏加载时间达到了8秒多,移动端用户基本就别想体验了。这还说什么性能优化,简直是倒退。我当时就想,Rollup不是以”小而美”著称吗,怎么到我这里就变成这样了?
找到病根了!
为了搞清楚到底哪里出了问题,我用了几个工具来分析:
- rollup-plugin-visualizer 来看包体积分布
- webpack-bundle-analyzer 的替代品来分析依赖关系
- Node.js 的 –inspect 参数来查看构建过程的CPU占用
结果发现,最大的问题是Tree Shaking没生效。我引入的lodash,结果整个库都被打包进去了,大概200多KB。还有moment.js的各种locale文件也被全量打包了。另外就是重复的polyfill代码,在各个chunk里都有,加起来也有不少体积。
几个核心优化手段
针对这些问题,我主要做了这几个优化,效果还不错:
1. Tree Shaking配置优化
原来我的package.json里设置了sideEffects: false,这直接禁用了所有模块的副作用检查,导致Tree Shaking完全失效。修改后:
{
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
同时在rollup.config.js里确保使用了正确的插件配置:
import { defineConfig } from 'rollup';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
export default defineConfig({
input: 'src/index.js',
output: {
dir: 'dist',
format: 'esm',
chunkFileNames: '[name]-[hash].js'
},
plugins: [
resolve({
browser: true,
dedupe: ['react', 'react-dom']
}),
commonjs(),
terser({
module: true,
compress: {
pure_getters: true,
unsafe_comps: true,
unsafe: true
},
mangle: {
properties: {
regex: /^__/,
}
},
output: {
comments: false
}
})
],
treeshake: {
propertyReadSideEffects: false,
tryCatchDeoptimization: false,
correctVarValueBeforeDeclaration: false
}
});
2. 动态导入按需分割
对于大组件或者路由级别的代码,我改成了动态导入。原来的代码是这样的:
// 优化前
import HeavyComponent from './HeavyComponent';
import ChartLibrary from 'chart.js';
import Editor from './components/Editor';
function App() {
return (
<div>
<MainContent />
{/* Editor组件只在特定页面才需要 */}
<Editor />
</div>
);
}
改成这样:
// 优化后
import { lazy, Suspense } from 'react';
const LazyEditor = lazy(() => import('./components/Editor'));
function App() {
return (
<div>
<MainContent />
<Suspense fallback={<div>Loading...</div>}>
<LazyEditor />
</Suspense>
</div>
);
}
3. 外部化大型依赖库
像lodash、moment这些库,我在CDN上引入,不打包到bundle里。rollup.config.js的externals配置:
export default {
external: [
'lodash',
'moment',
'react',
'react-dom',
'chart.js'
],
output: {
globals: {
lodash: '_',
moment: 'moment',
react: 'React',
'react-dom': 'ReactDOM',
'chart.js': 'Chart'
}
}
};
4. 压缩和混淆优化
Terser插件的配置是关键。上面的配置已经包含了比较激进的压缩选项,但要注意兼容性问题。另外我还用了@rollup/plugin-replace来移除开发相关的代码:
import replace from '@rollup/plugin-replace';
// 在plugins数组里加上
replace({
preventAssignment: true,
values: {
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.DEBUG': JSON.stringify(false)
}
})
性能数据对比
优化前后的数据变化还是很明显的:
- 主bundle大小:从2.3MB降到340KB(减少了85%)
- 构建时间:从12秒降到2.8秒(快了约75%)
- 首屏加载时间:从8.2秒降到1.1秒(快了87%)
- 首字节时间:从2.1秒降到320ms
数字不会骗人,优化效果还是挺显著的。特别是首屏加载时间,从8秒多降到了1秒多,用户体验提升很明显。
踩过的坑和注意事项
过程中踩了几个坑:
1. sideEffects配置要小心,一开始设错了导致一些CSS样式没加载。记得把样式文件加到sideEffects列表里。
2. externals配置的时候,如果CDN上的版本和本地安装的不一致,很容易出runtime错误。最好用版本锁定。
3. 动态导入的chunk命名要规范,不然部署后缓存策略会混乱。我用了[name]-[hash]的格式。
4. 测试环境要模拟真实网络条件,不然容易高估优化效果。我用了Chrome DevTools的Throttling功能来测试低速网络下的表现。
总的来说,Rollup的性能优化主要是围绕Tree Shaking和代码分割来做。配置正确的话,确实能带来不错的性能提升。当然,也要根据项目具体情况来调整,没有一成不变的最佳实践。
以上是我这次Rollup性能优化的经验分享,有更优的实现方式欢迎评论区交流。

暂无评论