核心目标:掌握 Vite 5 项目配置、TypeScript 类型实践、ESLint/Prettier 规范、环境变量管理、构建优化等工程化最佳实践。
📋 本章知识点
| 知识点 | 说明 | 难度 |
|---|---|---|
| Vite 配置 | 插件、别名、代理、构建选项 | ⭐⭐ |
| 环境变量 | .env 文件、import.meta.env |
⭐ |
| TypeScript | 泛型、工具类型、接口设计 | ⭐⭐⭐ |
| 目录规范 | 项目结构最佳实践 | ⭐ |
| ESLint + Prettier | 代码规范自动化 | ⭐⭐ |
| 构建优化 | 代码分割、Tree-shaking | ⭐⭐⭐ |
7.1 Vite 配置详解
Vite 是基于原生 ESM 的下一代前端构建工具,开发环境无需打包,极速启动。
完整配置示例
typescript
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), '')
return {
plugins: [vue()],
// 路径别名:@ → src/
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
// 开发服务器
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: env.VITE_API_BASE || 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建优化
build: {
target: 'es2015',
sourcemap: command === 'serve',
rollupOptions: {
output: {
// 手动分包:将大的第三方库单独打包
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
utils: ['axios', '@vueuse/core']
}
}
}
}
}
})
Vite vs Webpack 对比
| 特性 | Vite | Webpack |
|---|---|---|
| 开发启动 | 毫秒级(原生 ESM) | 秒~分钟(打包整个项目) |
| HMR 速度 | 极快(只更新变化模块) | 较慢(重新打包) |
| 配置复杂度 | 简单 | 复杂 |
| 构建产物 | Rollup(体积小) | Webpack(灵活) |
| 生态 | 兼容大多数 Rollup 插件 | 插件生态最丰富 |
7.2 环境变量管理
文件结构
bash
├── .env # 所有环境公共变量
├── .env.development # 开发环境(vite dev)
├── .env.staging # 测试环境(--mode staging)
└── .env.production # 生产环境(vite build)
变量定义规则
bash
# .env.development
VITE_APP_TITLE=Vue Demo(开发)
VITE_API_BASE=http://localhost:8080
VITE_ENABLE_MOCK=true
# .env.production
VITE_APP_TITLE=Vue Demo
VITE_API_BASE=https://api.example.com
VITE_ENABLE_MOCK=false
# ⚠️ 只有 VITE_ 前缀的变量才会暴露给浏览器
# 敏感变量(如数据库连接)不加 VITE_ 前缀,只在 Node 端使用
读取环境变量
typescript
// src/config/index.ts
export const AppConfig = {
title: import.meta.env.VITE_APP_TITLE,
apiBase: import.meta.env.VITE_API_BASE,
enableMock: import.meta.env.VITE_ENABLE_MOCK === 'true',
isDev: import.meta.env.DEV,
isProd: import.meta.env.PROD,
mode: import.meta.env.MODE, // 'development' | 'production' | 'staging'
}
// TypeScript 类型声明(env.d.ts)
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_API_BASE: string
readonly VITE_ENABLE_MOCK: string
}
7.3 TypeScript 类型实践
核心接口设计
typescript
// src/types/index.ts
// 用户模型
export interface User {
id: number
name: string
email: string
avatar?: string
role: 'admin' | 'editor' | 'viewer'
createdAt: Date
}
// 泛型 API 响应包装
export interface ApiResponse<T> {
code: number
data: T
message: string
timestamp: number
}
// 分页类型
export interface Pagination<T> {
list: T[]
total: number
page: number
pageSize: number
}
// 实用工具类型
export type CreateUserDto = Omit<User, 'id' | 'createdAt'>
export type UpdateUserDto = Partial<CreateUserDto>
export type UserSummary = Pick<User, 'id' | 'name' | 'role'>
// 函数类型
export type FetchFn<T> = (params?: Record<string, unknown>) => Promise<ApiResponse<T>>
Vue 组件中的 TypeScript 最佳实践
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import type { User, UpdateUserDto } from '@/types'
// defineProps 类型
const props = defineProps<{
user: User
readonly?: boolean
}>()
// withDefaults 设置默认值
const propsWithDefaults = withDefaults(defineProps<{
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
}>(), {
size: 'md',
disabled: false
})
// defineEmits 类型
const emit = defineEmits<{
update: [value: UpdateUserDto]
delete: [id: number]
cancel: []
}>()
// defineExpose 暴露方法给父组件
defineExpose({
focus: () => inputRef.value?.focus()
})
</script>
常用工具类型速查
typescript
// 从类型中选取/排除字段
type A = Pick<User, 'id' | 'name'> // { id: number; name: string }
type B = Omit<User, 'createdAt'> // 去掉 createdAt
// 将所有字段变为可选/必须
type C = Partial<User> // 全部可选
type D = Required<User> // 全部必须
// 只读
type E = Readonly<User>
// Record:构造对象类型
type F = Record<string, User> // { [key: string]: User }
// 条件类型
type IsAdmin<T extends User> = T['role'] extends 'admin' ? true : false
// infer 推断
type ReturnType<T> = T extends (...args: any) => infer R ? R : never
7.4 项目目录规范
vue_demos/
├── public/ # 不经 Vite 处理的静态资源
├── src/
│ ├── api/ # API 请求层(axios 封装)
│ │ ├── request.ts # axios 实例 + 拦截器
│ │ ├── user.ts # 用户相关 API
│ │ └── index.ts # 统一导出
│ │
│ ├── assets/ # 图片、字体等静态资源(Vite 处理)
│ │
│ ├── components/ # 公共组件
│ │ ├── base/ # 基础 UI(Button、Input、Modal...)
│ │ └── business/ # 业务组件(UserCard、OrderList...)
│ │
│ ├── composables/ # 可复用逻辑(useXxx 命名)
│ │ ├── useFetch.ts
│ │ ├── useDebounce.ts
│ │ └── useLocalStorage.ts
│ │
│ ├── router/ # Vue Router
│ │ └── index.ts
│ │
│ ├── stores/ # Pinia 状态管理
│ │ ├── user.ts
│ │ └── index.ts
│ │
│ ├── types/ # TypeScript 类型定义(只放类型)
│ │ └── index.ts
│ │
│ ├── utils/ # 纯工具函数(无副作用)
│ │ ├── format.ts # 日期、数字格式化
│ │ └── validate.ts # 表单验证
│ │
│ ├── views/ # 页面组件(路由级)
│ │ └── Chapter01_Basics/
│ │ └── BasicsDemo.vue
│ │
│ ├── App.vue
│ ├── main.ts
│ └── style.css # 全局样式 + CSS 变量
│
├── .env
├── .env.development
├── .env.production
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts
命名约定
| 类型 | 命名规范 | 示例 |
|---|---|---|
| 组件文件 | PascalCase | UserCard.vue |
| 页面文件 | PascalCase | HomeView.vue |
| Composable | camelCase,use 前缀 | useFetch.ts |
| Store | camelCase | useUserStore |
| 工具函数 | camelCase | formatDate.ts |
| 类型/接口 | PascalCase | interface User |
7.5 ESLint + Prettier
安装
bash
npm install -D eslint @vue/eslint-config-typescript @vue/eslint-config-prettier
ESLint 配置
javascript
// .eslintrc.cjs
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: { ecmaVersion: 'latest' },
rules: {
'vue/multi-word-component-names': 'off',
'vue/define-macros-order': ['error', {
order: ['defineOptions', 'defineProps', 'defineEmits', 'defineExpose']
}],
'@typescript-eslint/no-explicit-any': 'warn',
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
}
}
Prettier 配置
json
// .prettierrc.json
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"printWidth": 100,
"vueIndentScriptAndStyle": true
}
7.6 构建优化
代码分割策略
typescript
// vite.config.ts - manualChunks
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('vue')) return 'vendor-vue'
if (id.includes('axios')) return 'vendor-axios'
return 'vendor-other'
}
// 按路由拆分
if (id.includes('views/Chapter01')) return 'chapter-01'
if (id.includes('views/Chapter02')) return 'chapter-02'
}
}
}
构建产物分析
bash
# 安装分析插件
npm install -D rollup-plugin-visualizer
# vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer'
plugins: [
vue(),
visualizer({ open: true, filename: 'dist/stats.html' })
]
# 构建后自动打开分析报告
npm run build
性能数据对比
| 指标 | 优化前 | 优化后 | 改善 |
|---|---|---|---|
| 首屏 JS | 850KB | 120KB | -86% |
| 启动时间 | 4.2s | 0.8s | -81% |
| 构建时间 | 12s | 4s | -67% |
| Lighthouse 分数 | 62 | 94 | +32 |
📌 核心要点总结
- Vite 开发用 ESM、构建用 Rollup,兼顾速度和产物质量
- 环境变量 必须加
VITE_前缀才能在客户端读取,敏感信息不加前缀 - TypeScript 用泛型
ApiResponse<T>封装接口,用工具类型减少重复定义 - 目录结构 按职责分层:
api/→stores/→composables/→components/→views/ - 代码规范 用 ESLint 检查、Prettier 格式化,CI 中强制执行
- 构建优化
manualChunks拆分第三方库,路由懒加载,图片loading="lazy"
🔗 专栏链接 :Vue 3 全栈开发实战专栏
📦 项目源码资源 :点击下载项目源码