正在加载,请稍候…

Vite 构建优化:代码分割、Tree Shaking 与生产性能

掌握 Vite 构建优化技巧,包括代码分割、Tree Shaking、Rollup 配置和包分析,助您实现 95+ 的 Lighthouse 评分。

Vite 构建优化:代码分割、Tree Shaking 与生产性能

为什么 Vite 构建性能至关重要

Vite 的开发服务器以速度著称,但生产构建则另当别论。配置不当的 Vite 项目可能产生数兆字节的包,拖累 Lighthouse 评分,并在应用加载前就赶走用户。到 2026 年,用户会放弃加载时间超过 3 秒的页面,而 Google 的 Core Web Vitals 直接影响搜索排名。

本指南深入探讨 Vite 的生产构建流水线,并展示能将 Lighthouse 评分从 60 提升到 95+ 的具体配置。

Vite 构建优化:代码分割、Tree Shaking 与生产性能 插图

理解 Vite 的构建流水线

Vite 在生产构建中底层使用 Rollup。Rollup 中可用的所有优化技术都可以通过 build.rollupOptions 在 Vite 中使用。Vite 的开发模式使用 esbuild 实现近乎即时的转换;生产模式使用 Rollup 进行深度优化。它们是根本不同的流水线。

代码分割策略

手动分块——影响最大的改动

Vite 的默认策略是将所有 node_modules 内容放入一个单一的 vendor 块。对于中等规模的应用,这会产生一个 500KB+ 的块,必须在任何内容渲染之前下载并解析。

// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          // React 运行时——很少更改,永久缓存
          'react-vendor': ['react', 'react-dom'],
          // UI 组件库——体积大但稳定
          'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
          // 路由
          'router': ['react-router-dom'],
          // 工具库
          'utils': ['lodash-es', 'date-fns', 'zod'],
        },
      },
    },
  },
})

当你分发独立的块时,返回的用户在应用小更新后只需重新下载实际更改的块。react-vendor 块永远不会改变,并会从浏览器缓存中即时提供。

基于函数的分块(适用于大型应用)

function manualChunks(id: string) {
  if (id.includes('/src/pages/')) {
    const match = id.match(/\/pages\/([^/]+)/)
    if (match) return `page-${match[1]}`
  }
  if (id.includes('node_modules')) {
    if (id.includes('recharts') || id.includes('d3')) return 'charts-vendor'
    if (id.includes('@codemirror') || id.includes('monaco-editor')) return 'editor-vendor'
    if (id.includes('framer-motion')) return 'animation-vendor'
    return 'vendor'
  }
}

export default defineConfig({
  build: { rollupOptions: { output: { manualChunks } } },
})

基于路由的代码分割(配合 React.lazy)

// App.tsx
import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings  = lazy(() => import('./pages/Settings'))
const Analytics = lazy(() => import('./pages/Analytics'))

export function App() {
  return (
    <Suspense fallback={<PageSkeleton />}>
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/analytics" element={<Analytics />} />
      </Routes>
    </Suspense>
  )
}

从未访问 /analytics 的用户永远不会下载 280KB 的图表代码。

Vite 构建优化:代码分割、Tree Shaking 与生产性能 插图

Tree Shaking:让它真正生效

Tree shaking 可以消除死代码,但前提是依赖项使用 ES 模块且导出无副作用。

lodash 问题及解决方案

// 糟糕:导入整个 lodash(~70KB gzipped)
import _ from 'lodash'
const result = _.groupBy(items, 'category')

// 良好:lodash-es 支持 tree shaking
import { groupBy } from 'lodash-es'
const result = groupBy(items, 'category')

使用 rollup-plugin-visualizer 进行包分析

import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({
      filename: 'dist/bundle-analysis.html',
      gzipSize: true,
      brotliSize: true,
      open: true, // 构建后自动打开
    }),
  ],
})

运行 vite build,可视化工具会自动打开,显示交互式包树图。每个矩形代表一个模块;大小对应字节。通过它你可以发现隐藏在日期选择器中的 moment.js 及其完整语言包。

Rollup 高级配置

压缩与 esbuild 选项

export default defineConfig({
  build: {
    // esbuild 是默认选项——快速且集成良好
    minify: 'esbuild',

    // 在生产环境中移除 console/debugger 语句
    esbuildOptions: {
      drop: ['console', 'debugger'],
    },

    // 针对现代浏览器——更少的转译意味着更小的输出
    target: 'es2020',
  },
})

Vite 构建优化:代码分割、Tree Shaking 与生产性能 插图

资源配置

export default defineConfig({
  build: {
    assetsInlineLimit: 4096,  // 小于 4KB 的资源内联为 base64
    cssCodeSplit: true,        // 每个块独立 CSS,实现并行加载
    sourcemap: 'hidden',       // 用于错误监控的 source map,对用户不可见

    rollupOptions: {
      output: {
        chunkFileNames:  'assets/[name]-[hash].js',
        entryFileNames:  'assets/[name]-[hash].js',
        assetFileNames:  'assets/[name]-[hash].[ext]',
      },
    },
  },
})

构建性能:更快的 CI 流水线

export default defineConfig({
  optimizeDeps: {
    include: ['react', 'react-dom', 'react-router-dom'],
    exclude: ['@vite/client', '@vite/env'],
  },
  build: {
    rollupOptions: {
      maxParallelFileOps: 20,
    },
  },
})

环境特定配置

import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')

  return {
    define: {
      __APP_VERSION__: JSON.stringify(env.npm_package_version),
      __ENABLE_ANALYTICS__: mode === 'production',
    },
    build: {
      sourcemap: mode === 'staging' ? true : 'hidden',
      chunkSizeWarningLimit: 500, // KB — 对大块发出警告
    },
  }
})

衡量你的成果

# 构建并分析
vite build --mode production

# 启动服务并运行 Lighthouse
npm install -g serve
serve dist &
npx lighthouse http://localhost:3000 --view

需要跟踪的关键 Lighthouse 指标

  • FCP(首次内容绘制):目标 < 1.8s
  • LCP(最大内容绘制):目标 < 2.5s
  • TBT(总阻塞时间):目标 < 200ms
  • CLS(累积布局偏移):目标 < 0.1

实际效果:仪表盘应用案例研究

指标 优化前 优化后 变化
包大小 (gzip) 847 KB 312 KB −63%
块数量 1 8 +7
LCP 4.2s 1.8s −57%
TBT 680ms 120ms −82%
Lighthouse 评分 61 94 +33 分

最大的单一胜利:将 rechartsd3 移到它们自己的块中,并懒加载分析页面。

总结

Vite 构建优化在于理解浏览器如何加载代码。按稳定性分割 vendor 依赖,对用户可能永远不会访问的路由使用动态导入,通过可视化插件验证 tree shaking,并在每次重大更改后使用 Lighthouse 进行测量。十分钟的配置就能让你从“尚可”变为“快速”。