This repository has been archived on 2026-05-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
obsidian/DEV/VITE/优化实践.md
T
2026-04-20 22:47:51 +08:00

16 KiB

tags, create time
tags create time
DEV
VITE
performance
optimization
2026-04-18

Vite 优化实践

性能优化总览

graph TD
    A[Vite 性能优化] --> B[开发环境]
    A --> C[生产构建]
    A --> D[运行时优化]
    
    B --> B1[依赖预构建]
    B --> B2[智能缓存]
    B --> B3[热更新优化]
    
    C --> C1[代码分割]
    C --> C2[资源优化]
    C --> C3[构建配置]
    
    D --> D1[按需加载]
    D --> D2[缓存策略]
    D --> D3[监控分析]

开发环境优化

依赖预构建优化

智能依赖识别

import { defineConfig } from 'vite'

export default defineConfig({
  optimizeDeps: {
    // 手动指定需要预构建的依赖
    include: [
      'react',
      'react-dom',
      'antd',
      'lodash'
    ],
    // 排除不需要预构建的模块
    exclude: [
      // 已是 ESM 格式的包
      'vue',
      // 开发时频繁修改的包
      './src/common/*'
    ],
    // 强制重新构建
    force: process.env.NODE_ENV === 'development'
  }
})

预构建性能调优

export default defineConfig({
  optimizeDeps: {
    // esbuild 配置
    esbuildOptions: {
      target: 'es2020',
      // 保持类名和函数名,便于调试
      keepNames: true,
      // 启用打包内联
      bundle: true,
      // 外部化某些依赖
      external: ['some-large-library']
    }
  }
})

服务器配置优化

高效的代理配置

export default defineConfig({
  server: {
    proxy: {
      // API 代理
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        // 路径重写
        rewrite: (path) => path.replace(/^\/api/, ''),
        // 超时设置
        timeout: 30000,
        // 代理错误处理
        configure: (proxy, _options) => {
          proxy.on('error', (err, _req, res) => {
            res.end(JSON.stringify({ error: err.message }))
          })
        }
      },
      // 静态资源代理
      '/static': {
        target: 'http://localhost:9000',
        changeOrigin: true
      }
    }
  }
})

开发服务器性能

export default defineConfig({
  server: {
    // 禁用自动浏览器打开
    open: false,

    // 严格的端口模式
    strictPort: false,

    // 仅监听 localhost
    host: '127.0.0.1',

    // HMR 配置
    hmr: {
      // 覆盖层配置
      overlay: {
        runtimeErrors: false,     // 不显示运行时错误
        errors: true,             // 显示编译错误
        warnings: false           // 不显示警告
      }
    },

    // 文件监听优化
    watch: {
      usePolling: false,          // 禁用轮询(默认)
      interval: 100,              // 轮询间隔
      ignored: [
        '**/node_modules/**',     // 忽略 node_modules
        '**/.git/**',             // 忽略 .git 目录
        '**/dist/**',             // 忽略构建输出
        'coverage/**'             // 忽略测试覆盖率
      ]
    }
  }
})

缓存策略

依赖缓存优化

export default defineConfig({
  optimizeDeps: {
    // 缓存目录
    cacheDir: 'node_modules/.vite',

    // 锁文件
    lockfile: true,

    // 禁用缓存调试
    // force: true
  }
})

// 清理依赖缓存
// npm run dev -- --force

模块缓存配置

export default defineConfig({
  build: {
    // 模块预加载
    modulePreload: {
      polyfill: false,           // 禁用预加载 polyfill
      resolveDependencies: false // 禁用依赖解析
    }
  }
})

生产构建优化

代码分割策略

智能代码分割

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        // 手动代码分割配置
        manualChunks: (id) => {
          // 节点模块分割
          if (id.includes('node_modules')) {
            if (id.includes('react')) {
              return 'vendor-react'
            }
            if (id.includes('antd')) {
              return 'vendor-ui'
            }
            if (id.includes('lodash') || id.includes('dayjs')) {
              return 'vendor-utils'
            }
            return 'vendor-other'
          }

          // 应用代码分割
          if (id.includes('pages/')) {
            const pageName = id.split('/pages/')[1].split('/')[0]
            return `pages/${pageName}`
          }

          // 组件分割
          if (id.includes('components/')) {
            const componentName = id.split('/components/')[1].split('/')[0]
            return `components/${componentName}`
          }
        }
      }
    }
  }
})

按路由懒加载

// React Router 懒加载示例
import { lazy, Suspense } from 'react'

// 路由组件懒加载
const HomePage = lazy(() => import(/* webpackChunkName: "home" */ '@/pages/Home'))
const AboutPage = lazy(() => import(/* webpackChunkName: "about" */ '@/pages/About'))
const DashboardPage = lazy(() => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard'))

// 使用 Suspense 包裹
function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/about" element={<AboutPage />} />
        <Route path="/dashboard" element={<DashboardPage />} />
      </Routes>
    </Suspense>
  )
}

资源优化

CSS 代码分割

export default defineConfig({
  build: {
    cssCodeSplit: true,          // 启用 CSS 代码分割
    cssTarget: 'chrome80',       // CSS 目标浏览器
    cssMinify: 'lightningcss'     // CSS 压缩器
  },
  css: {
    devSourcemap: false,         // 开发环境不生成 Source Map
    
    // PostCSS 配置
    postcss: './postcss.config.js',
    
    preprocessorOptions: {
      scss: {
        api: 'modern-compiler'   // 使用现代编译器
      }
    }
  }
})

静态资源优化

export default defineConfig({
  build: {
    // 资源处理
    assetsInlineLimit: 4096,     // 小于 4kb 的资源内联
    
    rollupOptions: {
      output: {
        // 资源文件命名
        assetFileNames: (assetInfo) => {
          const info = assetInfo.name?.split('.') ?? []
          const extType = info[info.length - 1]
          
          if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name ?? '')) {
            return `media/[name]-[hash].[ext]`
          }
          if (/\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i.test(assetInfo.name ?? '')) {
            return `images/[name]-[hash].[ext]`
          }
          if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name ?? '')) {
            return `fonts/[name]-[hash].[ext]`
          }
          return `assets/[name]-[hash].[ext]`
        }
      }
    }
  }
})

压缩与优化

代码压缩配置

export default defineConfig({
  build: {
    minify: 'terser',           // 压缩器选择: terser | esbuild
    terserOptions: {
      compress: {
        // 移除 console
        drop_console: true,
        drop_debugger: true,
        
        // 纯函数优化
        pure_funcs: [
          'console.log',
          'console.info',
          'console.debug'
        ],
        
        // 代码优化
        ecma: 2020,
        arguments: true,
        dead_code: true,
        side_effects: true
      },
      mangle: {
        // 变量名混淆
        toplevel: true,
        properties: {
          regex: /^_/            // 混淆以下划线开头的属性
        },
        keep_classnames: false,
        keep_fnames: false
      },
      format: {
        // 保留版权注释
        comments: false,
        // 不移除代码
        preserveAnnotations: false
      }
    },
    
    // 不显示压缩后大小
    reportCompressedSize: false
  }
})

构建产物分析

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

export default defineConfig({
  plugins: [
    visualizer({
      filename: './dist/stats.html',
      open: true,
      gzipSize: true,
      brotliSize: true,
      template: 'treemap'        // treemap | sunburst | network
    })
  ]
})

// 构建后分析
// npm run build

运行时优化

按需导入

UI 组件按需导入

// Ant Design 按需导入
import { ConfigProvider } from 'antd'
import Button from 'antd/es/button'
import Input from 'antd/es/input'

// 或者使用自动导入插件
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    react(),
    Components({
      resolvers: [
        AntDesignVueResolver({
          importStyle: 'less'    // 按需导入样式
        })
      ]
    })
  ]
})

工具库按需导入

// 原始导入方式(导入整个库)
// import _ from 'lodash'
// const result = _.map(arr, (item) => item.value)

// 按需导入方式
import { map } from 'lodash-es'
const result = map(arr, (item) => item.value)

// 或者使用 lodash-unified-plugin

缓存策略

HTTP 缓存配置

// Nginx 配置示例
location / {
    try_files $uri $uri/ /index.html;
    
    # 关键资源缓存策略
    location ~* \.(js|css)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    
    # 图片资源缓存
    location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
        expires 6M;
        add_header Cache-Control "public, max-age=15552000";
    }
    
    # HTML 不缓存
    location ~* \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }
}

浏览器缓存优化

// 预加载关键资源
// 在 index.html 中添加
<link rel="modulepreload" href="/assets/index-[hash].js">
<link rel="prefetch" href="/assets/lazy-chunk-[hash].js">

// React 应用中的预加载
const preloadComponent = (path: string) => {
  const link = document.createElement('link')
  link.rel = 'prefetch'
  link.href = path
  document.head.appendChild(link)
}

// 根据路由预加载
const routes = [
  { path: '/dashboard', preload: () => preloadComponent('/assets/dashboard-[hash].js') }
]

性能监控

运行时性能监控

// 性能监控工具
class PerformanceMonitor {
  private metrics: Map<string, number> = new Map()

  measure(name: string, fn: () => void) {
    const start = performance.now()
    fn()
    const duration = performance.now() - start

    this.metrics.set(name, duration)
    
    // 开发环境打印性能指标
    if (import.meta.env.DEV) {
      console.log(`[Performance] ${name}: ${duration.toFixed(2)}ms`)
    }
  }

  getMetrics() {
    return Object.fromEntries(this.metrics)
  }
}

// 使用示例
const monitor = new PerformanceMonitor()
monitor.measure('app-init', () => {
  initializeApp()
})

构建 time analysis

import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    // 构建时间限制
    chunkSizeWarningLimit: 1000,
    
    // 构建并行化
    parallel: true,
    
    // 构建统计信息
    reportCompressedSize: false
  }
})

// 使用 time 命令测量构建时间
// time npm run build

实际案例分析

大型 React 项目优化

问题场景

  • 🔧 项目包含 200+ 组件
  • 📦 构建后的 main.js 超过 2MB
  • 🚀 首屏加载时间 5-8 秒

优化方案

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          // 核心框架
          'react-core': ['react', 'react-dom', 'react-router-dom'],
          
          // UI 组件库
          'antd-core': ['antd'],
          'antd-icons': ['@ant-design/icons'],
          
          // 状态管理
          'state-management': ['zustand', 'immer'],
          
          // 工具库
          'utils': ['lodash-es', 'dayjs', 'axios'],
          
          // 业务模块
          'business-user': [/src\/modules\/user/],
          'business-order': [/src\/modules\/order/],
          'business-product': [/src\/modules\/product/]
        }
      }
    }
  }
})

优化结果

  • ✅ main.js 减少到 800KB
  • ✅ 首屏加载时间降低到 2-3 秒
  • ✅ 构建时间从 3 分钟减少到 1 分钟

TypeScript 项目编译优化

编译速度优化

// tsconfig.json
{
  "compilerOptions": {
    "incremental": true,           // 增量编译
    "tsBuildInfoFile": ".tsbuildinfo",
    "skipLibCheck": true,          // 跳过类型声明文件检查
    "skipDefaultLibCheck": true
  },
  
  "references": [                   // 项目引用
    { "path": "./packages/component" }
  ]
}

类型检查性能

// 按需类型检查
// .githooks/pre-commit
#!/bin/bash
git diff --cached --name-only | grep '\.tsx?$' | xargs npx tsc --noEmit

// 或者在开发环境只检查当前文件
// IDE 配置 TypeScript Server 模式

组件库开发优化

库模式配置

// vite.config.ts (组件库)
export default defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'MyComponentLibrary',
      fileName: (format) => `my-component-library.${format}.js`,
      formats: ['es', 'umd', 'cjs']
    },
    rollupOptions: {
      // 外部化 react
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM'
        }
      }
    },
    // 压缩
    minify: 'terser',
    sourcemap: true
  }
})

监控与调试

开发环境监控

import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    // 性能监控插件
    {
      name: 'performance-monitor',
      transform(code, id) {
        // 监控大文件
        if (code.length > 100000) {
          console.warn(`[Large File] ${id}: ${code.length} bytes`)
        }
        return null
      }
    }
  ]
})

生产环境调试

// Source Map 配置
export default defineConfig({
  build: {
    sourcemap: true,               // 生产环境生成 Source Map
    sourceMapDebug: false
  }
})

// 部署环境配置错误的 Source Map
// 设置为 true 便于问题追踪,false 提高安全性

最佳实践总结

✅ 推荐实践

  1. 依赖管理

    • 使用 include 明确指定预构建依赖
    • 定期清理依赖缓存
    • 优先选择 ESM 格式的包
  2. 代码组织

    • 按路由和功能模块进行代码分割
    • 使用动态导入实现懒加载
    • 合理组织组件和工具函数
  3. 性能优化

    • 启用 Tree-shaking 移除死代码
    • 使用现代压缩工具
    • 优化资源加载策略
  4. 监控分析

    • 定期分析构建产物
    • 监控应用运行性能
    • 持续优化配置

❌ 避免问题

  1. 过度配置

    • 不要过度使用配置插件
    • 避免不必要的依赖
    • 保持配置简洁
  2. 忽略缓存

    • 不要忽略依赖缓存的清理
    • 注意缓存失效策略
    • 合理配置缓存时间
  3. 盲目优化

    • 不要在没有性能问题时过度优化
    • 优先解决真正的性能瓶颈
    • 基于数据驱动的优化决策

工具推荐

构建分析工具

  • vite-bundle-visualizer - 构建产物可视化
  • rollup-plugin-visualizer - 深度构建分析
  • source-map-explorer - Source Map 分析

性能测试工具

  • Lighthouse - 页面性能评估
  • WebPageTest - 多地性能测试
  • Chrome DevTools Performance - 运行时性能分析

优化是一个持续的过程,需要根据项目的具体情况和性能数据来调整策略。通过合理的配置和分析工具,可以让 Vite 项目在各种场景下表现优异。

相关文档: