Rollup打包配置踩坑记那些年我遇到的常见问题及解决方案

欧阳士媛 前端 阅读 1,617
赞 10 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

最近重构一个老项目的打包流程,用了Rollup来替换之前的Webpack。本来想着用Rollup的轻量级特性能让构建速度快一点,结果发现打包出来的文件居然比之前还要大,构建时间也慢得要命。开发环境热更新有时候要等个5-6秒才能看到效果,真是一言难尽。

Rollup打包配置踩坑记那些年我遇到的常见问题及解决方案

更离谱的是,生产环境下首屏加载时间达到了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性能优化的经验分享,有更优的实现方式欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论