Skip to content
On this page

Vite 最佳实践

本章总结了使用 Vite 进行项目开发的最佳实践,涵盖了配置优化、性能提升、开发工作流等多个方面。

项目结构

推荐的项目结构

my-vite-project/
├── index.html          # 主 HTML 文件
├── package.json
├── vite.config.js      # Vite 配置文件
├── .env*              # 环境变量文件
├── public/            # 静态资源(不经过构建处理)
│   ├── favicon.ico
│   └── robots.txt
└── src/               # 源代码目录
    ├── assets/        # 静态资源(经过构建处理)
    │   ├── images/
    │   ├── icons/
    │   └── styles/
    ├── components/    # 组件
    ├── composables/   # Vue 组合函数或 React Hooks
    ├── utils/         # 工具函数
    ├── composables/   # 可复用逻辑
    ├── views/         # 页面视图
    ├── router/        # 路由配置
    ├── store/         # 状态管理
    ├── styles/        # 全局样式
    ├── types/         # TypeScript 类型定义
    ├── main.js        # 应用入口
    └── App.vue        # 根组件

配置优化

基础配置

javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  // 基础路径(部署到子路径时使用)
  base: './',
  
  // 源码目录
  root: '.',
  
  // 构建配置
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    minify: 'esbuild',
    sourcemap: false,
  },
  
  // 路径别名
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'),
      '@assets': resolve(__dirname, './src/assets'),
      '@components': resolve(__dirname, './src/components'),
      '@utils': resolve(__dirname, './src/utils'),
    }
  },
  
  // 插件
  plugins: [vue()],
  
  // 开发服务器配置
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

环境特定配置

javascript
// vite.config.js
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode }) => {
  // 加载环境变量
  const env = loadEnv(mode, process.cwd(), '')
  
  return {
    base: env.VITE_BASE_URL || './',
    define: {
      __APP_ENV__: env.APP_ENV
    },
    server: {
      port: parseInt(env.VITE_PORT) || 3000,
      proxy: {
        '/api': {
          target: env.VITE_API_URL || 'http://localhost:8080',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        }
      }
    }
  }
})

性能优化

依赖预构建优化

javascript
// vite.config.js
export default {
  optimizeDeps: {
    include: [
      'vue',
      'vue-router',
      'pinia',
      'axios',
      'lodash-es'
    ],
    exclude: [
      'heavy-server-lib'  // 排除仅服务端使用的库
    ]
  }
}

代码分割

javascript
// 动态导入实现代码分割
// 路由级代码分割
const routes = [
  {
    path: '/',
    component: () => import('./views/Home.vue')
  },
  {
    path: '/about',
    component: () => import('./views/About.vue')
  }
]

// 组件级代码分割
const LazyComponent = defineAsyncComponent(() => 
  import('./components/HeavyComponent.vue')
)

// 库的代码分割
const loadChart = async () => {
  const { Chart } = await import('chart.js')
  return Chart
}

构建优化

javascript
// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          // 分离框架代码
          'vue': ['vue', 'vue-router'],
          // 分离工具库
          'utils': ['lodash-es', 'axios'],
          // 分离 UI 库
          'ui': ['element-plus', '@vueuse/core']
        },
        // 自定义文件名
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    },
    // 启用压缩
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
}

开发工作流

环境变量管理

text
# .env.development
VITE_API_URL=http://localhost:3000/api
VITE_APP_ENV=development
VITE_DEBUG=true

# .env.production
VITE_API_URL=https://api.myapp.com
VITE_APP_ENV=production
VITE_DEBUG=false

# .env.local (git 忽略)
VITE_API_URL=http://local-api:4000
VITE_CUSTOM_FEATURE=true

TypeScript 配置

json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    
    /* 路径映射 */
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@assets/*": ["src/assets/*"],
      "@components/*": ["src/components/*"]
    }
  },
  "include": ["src"]
}

ESLint 和 Prettier 集成

javascript
// .eslintrc.cjs
module.exports = {
  root: true,
  env: {
    node: true,
    'vue/setup-compiler-macros': true
  },
  extends: [
    'plugin:vue/vue3-essential',
    '@vue/eslint-config-typescript',
    '@vue/eslint-config-prettier'
  ],
  rules: {
    // 自定义规则
  }
}
json
// .prettierrc
{
  "semi": false,
  "tabWidth": 2,
  "singleQuote": true,
  "printWidth": 80,
  "trailingComma": "es5"
}

资源管理

静态资源处理

javascript
// 推荐的资源组织方式
// 图像资源 - 使用导入或 public 目录
import logo from '@/assets/logo.png'  // 小图像,会被优化
const bgImage = '/images/background.jpg'  // 大图像,放在 public

// SVG 图标处理
// 作为组件导入
import IconHome from '@/assets/icons/home.svg'

// 作为 URL 使用
import iconUrl from '@/assets/icons/menu.svg?url'

CSS 优化

css
/* 使用 CSS 变量便于主题切换 */
:root {
  --color-primary: #007bff;
  --color-secondary: #6c757d;
  --border-radius: 4px;
}

/* 启用 CSS 代码分割 */
/* styles/global.css */
@import './variables.css';
@import './mixins.css';

/* 组件样式 */
/* components/Button.css */
.component-button {
  border-radius: var(--border-radius);
  background: var(--color-primary);
}

测试配置

单元测试配置

javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode }) => {
  const isTest = mode === 'test'
  
  return {
    plugins: [vue()],
    test: isTest ? {
      environment: 'jsdom',
      setupFiles: ['./tests/setup.js'],
      include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}']
    } : {},
    resolve: {
      alias: isTest ? {
        '@': resolve(__dirname, './src'),
        ... // 测试专用别名
      } : {
        '@': resolve(__dirname, './src')
      }
    }
  }
})

部署优化

生产构建配置

javascript
// vite.config.js
export default {
  build: {
    // 启用子资源完整性
    sourcemap: false,  // 生产环境关闭 sourcemap
    cssTarget: 'chrome61',  // CSS 兼容目标
    
    rollupOptions: {
      output: {
        // 预加载策略
        format: 'es',
        
        // 文件大小优化
        manualChunks(id) {
          if (id.includes('node_modules')) {
            if (id.includes('vue')) {
              return 'vendor-vue'
            }
            if (id.includes('chart') || id.includes('d3')) {
              return 'charts'
            }
            return 'vendor'
          }
        }
      }
    }
  },
  
  // 静态资源优化
  assetsInclude: [
    // 根据需要包含额外的资源类型
  ]
}

预览服务器配置

javascript
// package.json
{
  "scripts": {
    "preview": "vite preview --port 4173",
    "preview:debug": "vite preview --port 4173 --strict-port"
  }
}

调试和监控

性能监控

javascript
// 性能监控插件示例
// plugins/performance.js
export default function performancePlugin() {
  return {
    name: 'performance',
    configResolved(config) {
      if (config.command === 'build') {
        const start = Date.now()
        this.load = () => {
          console.log(`构建耗时: ${Date.now() - start}ms`)
        }
      }
    }
  }
}

错误处理

javascript
// 全局错误处理
// main.js
if (import.meta.env.DEV) {
  // 开发环境错误处理
  window.addEventListener('error', (e) => {
    console.error('Runtime Error:', e.error)
  })
  
  window.addEventListener('unhandledrejection', (e) => {
    console.error('Unhandled Promise Rejection:', e.reason)
  })
}

常见陷阱和解决方案

1. 环境变量问题

javascript
// ❌ 错误 - 直接使用 process.env
const apiUrl = process.env.API_URL  // 不会在客户端生效

// ✅ 正确 - 使用 import.meta.env
const apiUrl = import.meta.env.VITE_API_URL

2. 路径问题

javascript
// ❌ 错误 - 相对路径在不同页面可能失效
img.src = './assets/image.png'

// ✅ 正确 - 使用导入或绝对路径
import imgUrl from '@/assets/image.png'
img.src = imgUrl

3. 动态导入优化

javascript
// 避免过于动态的导入
// ❌ 这样无法进行预构建
const module = await import(userInput)

// ✅ 预先定义可能的导入
const modules = {
  'chart': () => import('./charts/Chart.js'),
  'table': () => import('./tables/Table.js')
}

const loadModule = async (type) => {
  const factory = modules[type]
  return factory ? factory() : null
}

项目维护

依赖管理

json
// package.json - 使用精确版本控制
{
  "dependencies": {
    "vue": "^3.3.0",
    "vue-router": "^4.2.0"
  },
  "devDependencies": {
    "vite": "^4.4.0",
    "@vitejs/plugin-vue": "^4.2.0"
  }
}

Git 配置

text
# .gitignore
node_modules/
dist/
.vite/
.env.local
coverage/
*.log

通过遵循这些最佳实践,您可以构建出高性能、可维护的 Vite 项目,同时享受快速的开发体验。