Skip to content

Vite 配置与优化进阶指南

⚠️ 本文档部分内容已过时,正在更新中。请参考最新官方文档获取最新信息。

Vite 深度配置、性能优化与最佳实践 | 更新时间:2025-02

目录


核心配置

1. 基础配置

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

export default defineConfig({
  // 项目根目录
  root: process.cwd(),
  
  // 开发服务器配置
  server: {
    host: '0.0.0.0', // 监听所有地址
    port: 3000,
    open: true, // 自动打开浏览器
    cors: true, // 允许跨域
    
    // 代理配置
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    },
    
    // HMR 配置
    hmr: {
      overlay: true // 错误覆盖层
    }
  },
  
  // 预览服务器配置
  preview: {
    port: 4173,
    host: '0.0.0.0'
  },
  
  // 路径别名
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
      '@assets': path.resolve(__dirname, 'src/assets')
    },
    // 导入时想要省略的扩展名列表
    extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
  },
  
  // CSS 配置
  css: {
    // CSS 预处理器配置
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      },
      less: {
        modifyVars: {
          'primary-color': '#1890ff'
        },
        javascriptEnabled: true
      }
    },
    
    // CSS Modules 配置
    modules: {
      localsConvention: 'camelCase',
      scopeBehaviour: 'local',
      generateScopedName: '[name]__[local]___[hash:base64:5]'
    },
    
    // PostCSS 配置
    postcss: {
      plugins: [
        require('autoprefixer'),
        require('postcss-preset-env')({
          stage: 3,
          features: {
            'nesting-rules': true
          }
        })
      ]
    }
  },
  
  // 插件配置
  plugins: [
    vue()
  ],
  
  // 构建配置
  build: {
    target: 'es2015',
    outDir: 'dist',
    a
       assetFileNames: '[ext]/[name]-[hash].[ext]'
      }
    },
    
    // 压缩配置(推荐 esbuild,比 terser 快 20-40 倍,输出仅大 1-2%)
    minify: 'esbuild', // 如需更高压缩率可使用 'terser',但构建速度会显著下降
    
    // 分块大小警告限制
    chunkSizeWarningLimit: 1000
  },
  
  // 依赖优化
  optimizeDeps: {
    include: ['vue', 'vue-router', 'pinia'],
    exclude: ['your-local-package']
  }
})

2. 环境变量配置

bash
# .env
VITE_APP_TITLE=My App

# .env.development
VITE_API_BASE_URL=http://localhost:8080
VITE_APP_ENV=development

# .env.production
VITE_API_BASE_URL=https://api.example.com
VITE_APP_ENV=production
typescript
// 使用环境变量
console.log(import.meta.env.VITE_API_BASE_URL)
console.log(import.meta.env.MODE) // 'development' | 'production'
console.log(import.meta.env.DEV) // boolean
console.log(import.meta.env.PROD) // boolean

// 类型声明
// env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_BASE_URL: string
  readonly VITE_APP_TITLE: string
  readonly VITE_APP_ENV: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

性能优化

1. 依赖预构建优化

typescript
// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    // 强制预构建
    include: [
      'vue',
      'vue-router',
      'pinia',
      'axios',
      'lodash-es'
    ],
    
    // 排除预构建
    exclude: [
      'your-local-package'
    ],
    
    // 自定义 esbuild 配置
    esbuildOptions: {
      target: 'es2020',
      supported: {
        'top-level-await': true
      }
    }
  }
})

2. 代码分割优化

typescript
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 将 node_modules 中的代码单独打包
          if (id.includes('node_modules')) {
            // 提取大型库
            if (id.includes('element-plus')) {
              return 'element-plus'
            }
            if (id.includes('echarts')) {
              return 'echarts'
            }
            if (id.includes('@vue')) {
              return 'vue-vendor'
            }
            // 其他 node_modules 代码
            return 'vendor'
          }
          
          // 按路由分割
          if (id.includes('src/views')) {
            const match = /src\/views\/(.+)\//.exec(id)
            if (match) {
              return `view-${match[1]}`
            }
          }
        }
      }
    }
  }
})

3. 图片优化

typescript
// ⚠️ vite-plugin-imagemin 已停止维护,构建时常报错
// 推荐使用 unplugin-imagemin 替代:
// import imagemin from 'unplugin-imagemin/vite'
//
// export default defineConfig({
//   plugins: [
//     imagemin({
//       // 默认使用 sharp,无需安装系统依赖
//     })
//   ]
// })

// 以下为旧版 vite-plugin-imagemin 配置(已不推荐):
// import viteImagemin from 'vite-plugin-imagemin'
// ...

4. Gzip 压缩

typescript
// vite.config.ts
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240, // 大于 10KB 才压缩
      algorithm: 'gzip',
      ext: '.gz',
      deleteOriginFile: false
    })
  ]
})

5. CDN 加速

typescript
// vite.config.ts
import { Plugin as importToCDN } from 'vite-plugin-cdn-import'

export default defineConfig({
  plugins: [
    importToCDN({
      modules: [
        {
          name: 'vue',
          var: 'Vue',
          path: 'https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js'
        },
        {
          name: 'vue-router',
          var: 'VueRouter',
          path: 'https://cdn.jsdelivr.net/npm/vue-router@4.2.4/dist/vue-router.global.prod.js'
        }
      ]
    })
  ]
})

6. 构建分析

typescript
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'

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

插件开发

1. 基础插件结构

typescript
// my-plugin.ts
import { Plugin } from 'vite'

export default function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    
    // 应用模式
    apply: 'build', // 'serve' | 'build' | (config, env) => boolean
    
    // 配置解析前调用
    config(config, env) {
      return {
        // 返回部分配置(会与现有配置合并)
      }
    },
    
    // 配置解析后调用
    configResolved(resolvedConfig) {
      // 存储最终解析的配置
    },
    
    // 配置开发服务器
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        // 自定义中间件
        next()
      })
    },
    
    // 转换 index.html
    transformIndexHtml(html) {
      return html.replace(
        /<title>(.*?)<\/title>/,
        `<title>My App</title>`
      )
    },
    
    // 自定义模块解析
    resolveId(source) {
      if (source === 'virtual-module') {
        return source
      }
    },
    
    // 加载自定义模块
    load(id) {
      if (id === 'virtual-module') {
        return 'export default "This is virtual!"'
      }
    },
    
    // 转换模块代码
    transform(code, id) {
      if (id.endsWith('.vue')) {
        // 转换 Vue 文件
        return {
          code: transformedCode,
          map: null
        }
      }
    },
    
    // 构建结束时调用
    buildEnd() {
      // 清理工作
    }
  }
}

2. 实战:自动导入插件

typescript
// auto-import-plugin.ts
import { Plugin } from 'vite'
import MagicString from 'magic-string'

export default function autoImportPlugin(): Plugin {
  const imports = new Map([
    ['ref', 'vue'],
    ['reactive', 'vue'],
    ['computed', 'vue'],
    ['watch', 'vue']
  ])

  return {
    name: 'auto-import',
    
    transform(code, id) {
      if (!id.endsWith('.vue') && !id.endsWith('.ts')) {
        return null
      }

      const s = new MagicString(code)
      const usedImports = new Set<string>()

      // 检测使用的 API
      imports.forEach((source, name) => {
        const regex = new RegExp(`\\b${name}\\b`, 'g')
        if (regex.test(code)) {
          usedImports.add(name)
        }
      })

      // 生成导入语句
      if (usedImports.size > 0) {
        const importStatement = `import { ${Array.from(usedImports).join(', ')} } from 'vue';\n`
        s.prepend(importStatement)
      }

      return {
        code: s.toString(),
        map: s.generateMap({ hires: true })
      }
    }
  }
}

3. 实战:Markdown 插件

typescript
// markdown-plugin.ts
import { Plugin } from 'vite'
import MarkdownIt from 'markdown-it'

export default function markdownPlugin(): Plugin {
  const md = new MarkdownIt()

  return {
    name: 'markdown',
    
    // 处理 .md 文件
    transform(code, id) {
      if (!id.endsWith('.md')) {
        return null
      }

      const html = md.render(code)
      
      return {
        code: `export default ${JSON.stringify(html)}`,
        map: null
      }
    }
  }
}

// 使用
import content from './README.md'
console.log(content) // HTML 字符串

生产构建优化

1. 完整的生产配置

typescript
// vite.config.prod.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteCompression from 'vite-plugin-compression'
import { visualizer } from 'rollup-plugin-visualizer'
// ⚠️ vite-plugin-imagemin 已停止维护,推荐使用 unplugin-imagemin
// import imagemin from 'unplugin-imagemin/vite'

export default defineConfig({
  mode: 'production',
  
  build: {
    target: 'es2015',
    outDir: 'dist',
    assetsDir: 'assets',
    assetsInlineLimit: 4096,
    cssCodeSplit: true,
    sourcemap: false,
    
    rollupOptions: {
      output: {
        manualChunks: {
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'ui-vendor': ['element-plus'],
          'utils': ['lodash-es', 'dayjs', 'axios']
        },
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: (assetInfo) => {
          const info = assetInfo.name.split('.')
          let extType = info[info.length - 1]
          
          if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name)) {
            extType = 'media'
          } else if (/\.(png|jpe?g|gif|svg|ico|webp)(\?.*)?$/i.test(assetInfo.name)) {
            extType = 'img'
          } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) {
            extType = 'fonts'
          }
          
          return `${extType}/[name]-[hash].[ext]`
        }
      }
    },
    
    minify: 'esbuild', // 推荐 esbuild(默认值),比 terser 快 20-40 倍
    // 如需更高压缩率,可切换为 terser(但构建速度会显著下降)
    // minify: 'terser',
    // terserOptions: {
    //   compress: {
    //     drop_console: true,
    //     drop_debugger: true,
    //     pure_funcs: ['console.log']
    //   },
    //   format: {
    //     comments: false
    //   }
    // },
    
    chunkSizeWarningLimit: 1000,
    reportCompressedSize: false // 禁用 gzip 压缩大小报告
  },
  
  plugins: [
    vue(),
    
    // Gzip 压缩
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz'
    }),
    
    // Brotli 压缩
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'brotliCompress',
      ext: '.br'
    }),
    
    // 图片压缩(已改用 unplugin-imagemin)
    // imagemin({
    //   // 默认使用 sharp,无需安装系统依赖
    // }),
    
    // 构建分析
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true,
      filename: 'dist/stats.html'
    })
  ]
})

2. 多页面应用配置

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import { resolve } from 'path'

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        admin: resolve(__dirname, 'admin/index.html'),
        mobile: resolve(__dirname, 'mobile/index.html')
      }
    }
  }
})

3. Library 模式

typescript
// vite.config.ts
import { defineConfig } from 'vite'
import { resolve } from 'path'

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      name: 'MyLib',
      fileName: (format) => `my-lib.${format}.js`,
      formats: ['es', 'cjs', 'umd']
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  }
})

常见问题

1. 解决 CommonJS 依赖问题

typescript
// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    include: ['problematic-package']
  },
  
  build: {
    commonjsOptions: {
      include: [/problematic-package/, /node_modules/]
    }
  }
})

2. 解决路径别名问题

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

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '~': path.resolve(__dirname, 'node_modules')
    }
  }
})

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "~/*": ["node_modules/*"]
    }
  }
}

3. 解决动态导入问题

typescript
// 使用 import.meta.glob
const modules = import.meta.glob('./modules/*.ts')

// 懒加载
const modules = import.meta.glob('./modules/*.ts', { eager: false })

// 直接导入
const modules = import.meta.glob('./modules/*.ts', { eager: true })

// 自定义导入
const modules = import.meta.glob('./modules/*.ts', {
  import: 'setup',
  eager: true
})

4. 解决环境变量类型问题

typescript
// env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_API_BASE_URL: string
  readonly VITE_APP_TITLE: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

5. 解决 HMR 问题

typescript
// 接受自身更新
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // 更新逻辑
  })
}

// 接受依赖更新
if (import.meta.hot) {
  import.meta.hot.accept('./dep.js', (newDep) => {
    // 更新逻辑
  })
}

// 处理更新失败
if (import.meta.hot) {
  import.meta.hot.dispose(() => {
    // 清理副作用
  })
}

最佳实践

1. 开发环境优化

typescript
// vite.config.ts
export default defineConfig({
  server: {
    // 预热常用文件
    warmup: {
      clientFiles: [
        './src/components/**/*.vue',
        './src/utils/**/*.ts'
      ]
    }
  },
  
  optimizeDeps: {
    // 强制预构建
    include: ['vue', 'vue-router', 'pinia']
  }
})

2. 生产环境优化

typescript
// vite.config.ts
export default defineConfig({
  build: {
    // 禁用 CSS 代码分割(如果 CSS 很小)
    cssCodeSplit: false,
    
    // 启用 CSS 压缩
    cssMinify: true,
    
    // 禁用 brotli 压缩大小报告(加快构建)
    reportCompressedSize: false
  }
})

3. 性能监控

typescript
// vite.config.ts
export default defineConfig({
  plugins: [
    {
      name: 'performance-monitor',
      buildStart() {
        this.startTime = Date.now()
      },
      buildEnd() {
        console.log(`Build time: ${Date.now() - this.startTime}ms`)
      }
    }
  ]
})

参考资料


最新知识补充(2024-2025)

1. Vite 6 Environment API

Vite 6(2024 年 11 月发布)引入了 Environment API,允许在单个 Vite Dev Server 中配置多个运行时环境(如客户端 + SSR),实现更灵活的模块处理。

typescript
// vite.config.ts - Environment API 示例
import { defineConfig } from 'vite'

export default defineConfig({
  environments: {
    client: {
      // 客户端环境配置
      build: {
        outDir: 'dist/client',
      },
    },
    ssr: {
      // SSR 环境配置
      build: {
        outDir: 'dist/ssr',
      },
    },
  },
})

2. Rolldown:统一开发与生产构建管线

Vite 当前开发使用 esbuild、生产使用 Rollup,导致行为差异。Rolldown(基于 Rust 开发)将统一两端管线:

  • 与 Rollup API 兼容
  • 开发和生产共享同一打包器,消除行为差异
  • 预计在 Vite 未来版本中替代 Rollup 作为生产打包器
typescript
// 未来 Vite 配置可能支持
export default defineConfig({
  build: {
    // rolldownMode: true, // 实验性,未来默认启用
  },
})

3. Lightning CSS 替代 PostCSS

Vite 5.4+ 支持 css.lightningcss 选项,使用 Rust 编写的 Lightning CSS 替代 PostCSS 进行 CSS 转换,性能大幅提升:

typescript
// vite.config.ts
import lightningcss from 'lightningcss'

export default defineConfig({
  css: {
    transformer: 'lightningcss',
    lightningcss: {
      // CSS 目标浏览器
      targets: lightningcss.browserslistToTargets(['> 0.2%', 'not dead']),
    },
  },
  build: {
    cssMinify: 'lightningcss', // 使用 Lightning CSS 压缩
  },
})

4. 压缩工具对比

工具构建速度输出大小说明
esbuild极快(基准)较大 1-2%Vite 默认,推荐大多数场景
terser慢 20-40 倍最小需极致压缩时使用
Lightning CSS极快与 esbuild 相当仅 CSS 压缩,Vite 5.4+

5. vite-plugin-imagemin 已停止维护

vite-plugin-imagemin 依赖系统级工具(gifsicle、optipng 等),安装时常出错且已停止维护。

推荐替代

  • unplugin-imagemin:基于 sharp,无需系统依赖,跨平台兼容
  • vite-plugin-static-copy + 外部压缩:CI 中单独处理图片

基于 VitePress 构建