banner
Hi my new friend!

Vite 构建产物异常排查实录

Scroll down

问题背景

最近在一个中型管理后台项目(约 40 个页面,200+ 组件)中,我遇到了一个棘手的问题:开发环境一切正常,但执行 vite build 部署到生产环境后,页面出现间歇性白屏。

错误信息在控制台中短暂闪现后消失,只留下一个空白的页面。这让问题定位变得非常困难。

排查过程

第一步:确认是否为环境差异

首先排除了最常见的几个原因:

  • 环境变量.env.production 配置正确,API 地址指向生产环境
  • 路由模式:使用的是 hash 路由,不存在 Nginx 配置问题
  • CDN 缓存:刷新后清除缓存问题依然存在

既然基本方向没有问题,我将目光转向了构建产物本身。

第二步:分析构建产物

通过 vite build --debug 输出详细的构建日志,我发现了一个关键线索:

dist/assets/index-abc123.js    2.8 MB  (gzip: 856 KB)
dist/assets/vendor-def456.js   4.2 MB  (gzip: 1.3 MB)

vendor chunk 高达 4.2MB。这严重影响了首屏加载性能,也可能导致了浏览器解析超时。

第三步:深入排查依赖打包

使用 rollup-plugin-visualizer 生成打包体积分析图后,真相浮出水面:

  1. Moment.js 全量引入:moment 的所有 locale 文件都被打包进来了(约 300KB gzip)
  2. Ant Design Vue 全量导入:没有使用按需加载,导致整个组件库被打包
  3. Lodash 全量引入:import 方式为 import _ from 'lodash' 而非 import debounce from 'lodash/debounce'

第四步:制定修复方案

针对以上三个问题,逐一修复:

修复 moment.js 体积问题

ts
// vite.config.ts
export default defineConfig({
  resolve: {
    alias: {
      // 使用 dayjs 替代 moment.js,体积减小 90%
    }
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-ui': ['ant-design-vue'],
          'vendor-utils': ['dayjs', 'lodash-es']
        }
      }
    }
  }
})

dayjs 替换 moment.js 后,日期处理相关代码的体积从 ~300KB 降低到 ~7KB。

修复 Ant Design Vue 导入方式

配置 unplugin-vue-components 实现自动按需导入:

ts
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    Components({
      resolvers: [AntDesignVueResolver()]
    })
  ]
})

修复 Lodash 导入方式

将项目中的 import _ from 'lodash' 全局替换为 import { debounce } from 'lodash-es',配合 Tree Shaking 去除未使用代码。

第五步:优化 chunk 分割策略

除了修复上述依赖问题,我还重新设计了 chunk 分割策略:

ts
build: {
  rollupOptions: {
    output: {
      manualChunks(id) {
        if (id.includes('node_modules')) {
          // 将大依赖拆分为独立 chunk
          if (id.includes('ant-design-vue')) return 'antd'
          if (id.includes('echarts')) return 'echarts'
          if (id.includes('@antv')) return 'antv'
          // 小型工具库合并
          return 'vendor'
        }
      }
    }
  }
}

最终效果

指标优化前优化后降幅
总包体积 (gzip)2.1 MB680 KB67.6%
最大单 chunk (gzip)1.3 MB280 KB78.5%
首屏加载时间4.8s1.2s75%
Lighthouse 评分4291+49

经验总结

  1. 开发环境 ≠ 生产环境:Vite 开发模式使用原生 ESM,而生产构建使用 Rollup 打包,两者的模块加载机制完全不一样
  2. 依赖体积要持续关注:建议在 CI 中集成 bundlesize 检查,防止依赖膨胀
  3. Tree Shaking 不是银弹:需要配合正确的导入方式(使用具名导入而非默认导入)才能生效
  4. chunk 分割策略需要针对性设计:一刀切的 splitVendorChunk 往往不够,需要根据业务场景手动配置
  • 本文作者:async
  • 本文链接:
  • 版权声明:本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。
其他文章
cover
页面设计原则
  • 26-06-02
  • 21:02
  • 设计