Vite

101 阅读4分钟

1. Vite 基础概念

Vite 是一个现代化的前端构建工具,主要特点:

  • 开发环境下使用原生 ES 模块导入

  • 生产环境使用 Rollup 打包

  • 快速的冷启动和热更新

  • 开箱即用的各种优化

2. 项目创建和基础配置

# 创建项目
npm create vite@latest my-vue-app -- --template vue-ts

# 或者使用 yarn
yarn create vite my-vue-app --template vue-ts

# 支持的模板
# vanilla, vanilla-ts, vue, vue-ts, react, react-ts, preact, preact-ts, lit, lit-ts, svelte, svelte-ts
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

export default defineConfig({
  // 插件
  plugins: [vue()],
  
  // 解析配置
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '~': path.resolve(__dirname, 'node_modules')
    },
    extensions: ['.js', '.ts', '.jsx', '.tsx', '.json']
  },
  
  // 服务器配置
  server: {
    host: '0.0.0.0',
    port: 3000,
    open: true,
    https: false,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },
  
  // 构建配置
  build: {
    target: 'es2015',
    outDir: 'dist',
    assetsDir: 'assets',
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-router', 'pinia'],
          'lodash': ['lodash-es']
        }
      }
    }
  }
})

3. 环境变量配置

# .env
VITE_APP_TITLE=My App
VITE_API_URL=http://api.example.com

# .env.development
VITE_APP_TITLE=Dev App
VITE_API_URL=http://dev-api.example.com

# .env.production
VITE_APP_TITLE=Prod App
VITE_API_URL=http://prod-api.example.com
// vite-env.d.ts
/// <reference types="vite/client" />

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

interface ImportMeta {
  readonly env: ImportMetaEnv
}

4. 插件配置

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import legacy from '@vitejs/plugin-legacy'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'

export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    
    // 浏览器兼容性
    legacy({
      targets: ['ie >= 11'],
      additionalLegacyPolyfills: ['regenerator-runtime/runtime']
    }),
    
    // 自动导入
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      dts: 'src/auto-imports.d.ts',
      resolvers: [ElementPlusResolver()]
    }),
    
    // 组件自动注册
    Components({
      dirs: ['src/components'],
      extensions: ['vue'],
      dts: 'src/components.d.ts',
      resolvers: [
        ElementPlusResolver(),
        IconsResolver({
          enabledCollections: ['ep']
        })
      ]
    }),
    
    // 图标
    Icons({
      autoInstall: true
    })
  ]
})

5. CSS 配置

// vite.config.ts
export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @import "@/styles/variables.scss";
          @import "@/styles/mixins.scss";
        `
      }
    },
    modules: {
      localsConvention: 'camelCaseOnly',
      scopeBehaviour: 'local',
      generateScopedName: '[name]_[local]_[hash:base64:5]'
    },
    postcss: {
      plugins: [
        autoprefixer(),
        postcssPresetEnv()
      ]
    }
  }
})

6. 优化配置

// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    include: ['lodash-es', '@vueuse/core'],
    exclude: ['vue-demi']
  },
  
  build: {
    // 分块策略
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString()
          }
        }
      }
    },
    
    // 压缩配置
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // 文件大小警告限制
    chunkSizeWarningLimit: 1500,
    
    // 静态资源处理
    assetsInlineLimit: 4096
  }
})

7. 开发调试配置

// vite.config.ts
export default defineConfig({
  server: {
    host: true,
    port: 3000,
    open: true,
    cors: true,
    
    // 代理配置
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    },
    
    // HMR 配置
    hmr: {
      overlay: true
    }
  }
})

8. 生产部署配置

// vite.config.ts
export default defineConfig({
  base: process.env.NODE_ENV === 'production' ? '/my-app/' : '/',
  
  build: {
    // 生成静态资源的存放目录
    assetsDir: 'static',
    
    // 小于此阈值的导入或引用资源将内联为 base64 编码
    assetsInlineLimit: 4096,
    
    // 启用/禁用 CSS 代码拆分
    cssCodeSplit: true,
    
    // 构建后是否生成 source map 文件
    sourcemap: false,
    
    // 自定义底层的 Rollup 打包配置
    rollupOptions: {
      input: {
        main: path.resolve(__dirname, 'index.html'),
        nested: path.resolve(__dirname, 'nested/index.html')
      },
      output: {
        chunkFileNames: 'static/js/[name]-[hash].js',
        entryFileNames: 'static/js/[name]-[hash].js',
        assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
      }
    }
  }
})

9. 常见问题和解决方案

  1. 路径别名问题
// vite.config.ts
import path from 'path'

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

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
  1. 类型声明问题
// src/shims-vue.d.ts
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

// src/shims-svg.d.ts
declare module '*.svg' {
  const content: any
  export default content
}
  1. HMR 问题
// vite.config.ts
export default defineConfig({
  server: {
    hmr: {
      overlay: false,
      timeout: 30000
    }
  }
})

10. 性能优化建议

  1. 依赖预构建
// vite.config.ts
export default defineConfig({
  optimizeDeps: {
    include: [
      'vue',
      'vue-router',
      'pinia',
      '@vueuse/core',
      'lodash-es'
    ]
  }
})
  1. 代码分割
// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['vue', 'vue-router', 'pinia'],
          'utils': ['lodash-es', '@vueuse/core']
        }
      }
    }
  }
})


3. **资源压缩**
```typescript
// vite.config.ts
import viteCompression from 'vite-plugin-compression'

export default defineConfig({
  plugins: [
    viteCompression({
      verbose: true,
      disable: false,
      threshold: 10240,
      algorithm: 'gzip',
      ext: '.gz'
    })
  ]
})

1. 高级插件配置

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import viteImagemin from 'vite-plugin-imagemin' // 图片压缩插件
import { visualizer } from 'rollup-plugin-visualizer' // 打包分析插件
import viteEslint from 'vite-plugin-eslint' // ESLint 插件
import viteMockServe from 'vite-plugin-mock' // Mock 数据插件

export default defineConfig({
  plugins: [
    vue(), // Vue 插件,处理 .vue 文件
    
    // 图片压缩配置
    viteImagemin({
      // GIF 图片压缩配置
      gifsicle: {
        optimizationLevel: 7, // 压缩级别 1-7,7 压缩率最高
        interlaced: false // 是否隔行扫描
      },
      // PNG 图片压缩配置
      optipng: {
        optimizationLevel: 7 // 压缩级别 0-7,7 压缩率最高
      },
      // JPEG 图片压缩配置
      mozjpeg: {
        quality: 70 // 压缩质量 0-100,值越小压缩率越高
      },
      // PNG 图片压缩配置(另一个压缩器)
      pngquant: {
        quality: [0.8, 0.9], // 压缩质量范围
        speed: 4 // 压缩速度 1-11,1 最慢但压缩率最高
      },
      // SVG 图片压缩配置
      svgo: {
        plugins: [
          {
            name: 'removeViewBox' // 移除 viewBox 属性
          },
          {
            name: 'removeEmptyAttrs', // 移除空属性
            active: false // 禁用此插件
          }
        ]
      }
    }),
    
    // 打包分析配置
    visualizer({
      filename: './stats.html', // 分析报告输出路径
      open: true, // 自动打开报告
      gzipSize: true // 显示 gzip 后的大小
    }),
    
    // ESLint 配置
    viteEslint({
      // 指定需要检查的文件
      include: ['src/**/*.ts', 'src/**/*.vue', 'src/*.ts', 'src/*.vue']
    }),
    
    // Mock 服务配置
    viteMockServe({
      mockPath: 'mock', // mock 文件目录
      supportTs: true, // 支持 TypeScript
      watchFiles: true, // 监听文件变化
      localEnabled: command === 'serve', // 开发环境启用
      prodEnabled: command === 'build' // 生产环境启用
    })
  ]
})

2. 高级构建配置

// vite.config.ts
export default defineConfig({
  build: {
    // 库模式构建配置
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'), // 入口文件
      name: 'MyLib', // 库的全局变量名
      fileName: (format) => `my-lib.${format}.js`, // 输出文件名
      formats: ['es', 'umd'] // 输出格式
    },
    
    // Rollup 构建配置
    rollupOptions: {
      external: ['vue'], // 外部依赖
      output: {
        globals: {
          vue: 'Vue' // 外部依赖的全局变量名
        },
        // 自定义代码分割策略
        manualChunks: (id) => {
          // 处理 node_modules 中的模块
          if (id.includes('node_modules')) {
            if (id.includes('lodash')) {
              return 'lodash' // lodash 单独打包
            }
            if (id.includes('@vueuse')) {
              return 'vueuse' // vueuse 单独打包
            }
            return 'vendor' // 其他第三方库打包到 vendor
          }
          // 处理源码模块
          if (id.includes('src/components')) {
            return 'components' // 组件单独打包
          }
          if (id.includes('src/utils')) {
            return 'utils' // 工具函数单独打包
          }
        }
      }
    },
    
    target: ['es2015', 'chrome87'], // 目标环境
    
    cssCodeSplit: true, // 启用 CSS 代码分割
    
    sourcemap: true, // 生成 source map
    
    emptyOutDir: true, // 构建前清空输出目录
    
    brotliSize: true // 启用 brotli 压缩并显示大小
  }
})

3. 高级开发配置

// vite.config.ts
export default defineConfig({
  server: {
    // HTTPS 配置
    https: {
      key: fs.readFileSync('path/to/server.key'), // SSL 私钥
      cert: fs.readFileSync('path/to/server.crt') // SSL 证书
    },
    
    // 代理配置
    proxy: {
      '/api': {
        target: 'http://localhost:8080', // 目标服务器
        changeOrigin: true, // 修改请求头中的 Origin
        rewrite: (path) => path.replace(/^\/api/, ''), // 重写请求路径
        configure: (proxy, options) => {
          // 自定义代理配置
          proxy.on('proxyReq', (proxyReq, req, res) => {
            // 添加自定义请求头
            proxyReq.setHeader('x-custom-header', 'custom-value')
          })
        }
      }
    },
    
    // 文件监听配置
    watch: {
      // 排除不需要监听的文件
      ignored: ['**/node_modules/**', '**/.git/**']
    },
    
    // 中间件配置
    middlewares: [
      (req, res, next) => {
        // 自定义请求处理
        if (req.url?.startsWith('/custom')) {
          res.end('Custom response')
        } else {
          next() // 继续下一个中间件
        }
      }
    ]
  }
})

4. 自定义插件开发

// plugins/vite-plugin-custom.ts
import type { Plugin } from 'vite'

export default function customPlugin(options = {}): Plugin {
  let config // 存储 Vite 配置
  
  return {
    name: 'vite-plugin-custom', // 插件名称
    
    // 配置解析完成后的钩子
    configResolved(resolvedConfig) {
      config = resolvedConfig
    },
    
    // 代码转换钩子
    transform(code, id) {
      if (id.endsWith('.vue')) {
        // 处理 Vue 文件的代码
        return {
          code: transformedCode, // 转换后的代码
          map: null // source map
        }
      }
    },
    
    // 开发服务器配置钩子
    configureServer(server) {
      server.middlewares.use((req, res, next) => {
        // 添加自定义中间件逻辑
        next()
      })
    },
    
    // 构建开始钩子
    buildStart() {
      // 构建开始时的初始化工作
    },
    
    // 构建结束钩子
    buildEnd() {
      // 构建结束时的清理工作
    }
  }
}

5. 性能优化进阶

// vite.config.ts
export default defineConfig({
  // 依赖预构建配置
  optimizeDeps: {
    // 强制预构建的依赖
    include: [
      'vue',
      'vue-router',
      'pinia',
      '@vueuse/core',
      'lodash-es'
    ],
    exclude: ['your-local-package'], // 排除预构建的依赖
    esbuildOptions: {
      target: 'es2015' // esbuild 目标环境
    }
  },
  
  // 构建配置
  build: {
    // 多页面应用入口配置
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        nested: resolve(__dirname, 'nested/index.html')
      },
      output: {
        // 自定义代码分割策略
        manualChunks(id) {
          if (id.includes('node_modules')) {
            const packageName = id
              .toString()
              .split('node_modules/')[1]
              .split('/')[0]
            
            // 根据包大小决定是否单独分割
            if (largePackages.includes(packageName)) {
              return `vendor-${packageName}` // 大型包单独分割
            }
            return 'vendor' // 其他第三方库打包到 vendor
          }
        }
      }
    },
    
    target: 'es2015', // 构建目标环境
    minify: 'terser', // 使用 terser 压缩
    terserOptions: {
      compress: {
        drop_console: true, // 移除 console
        drop_debugger: true // 移除 debugger
      }
    },
    
    cssCodeSplit: true, // CSS 代码分割
    cssMinify: true // CSS 压缩
  }
})