路径别名与代理
路径别名与代理 🔗
在 Vite 项目中,路径别名(alias)和开发代理(proxy)是两个高频使用的配置。本章我们详细学习它们的配置方式与最佳实践。
一、路径别名 💎
路径别名可以让我们使用 @/components/Button 替代 src/components/Button,让代码更清晰、维护更方便。
配置 alias 💎
// vite.config.ts
import { defineConfig } from 'vite'
import path from 'node:path'
export default defineConfig({
resolve: {
alias: {
// 简写
'@': path.resolve(__dirname, 'src'),
// 等价于(推荐写法,跨平台兼容)
// '@': fileURLToPath(new URL('./src', import.meta.url)),
// 自定义别名
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
'@assets': path.resolve(__dirname, 'src/assets'),
'@views': path.resolve(__dirname, 'src/views'),
'@styles': path.resolve(__dirname, 'src/styles'),
},
},
})跨平台兼容
在 Windows 上,__dirname 可能无法正常使用。推荐使用 import.meta.url:
import { fileURLToPath, URL } from 'node:url'
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})简化配置(推荐) 💎
如果觉得 path.resolve 繁琐,可以封装一个工具函数:
// vite.config.ts
import { defineConfig } from 'vite'
import path from 'node:path'
// 自动获取项目根目录(兼容 Windows / macOS / Linux)
const r = (p: string) => path.resolve(__dirname, p)
export default defineConfig({
resolve: {
alias: {
'@': r('src'),
'@components': r('src/components'),
'@utils': r('src/utils'),
},
},
})TypeScript 类型支持 💎
vite.config.ts 中配置 alias 后,编译时 别名可以正常使用,但 TypeScript 类型检查 还需在 tsconfig.json 中配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}Vue CLI / Vite 项目的差异
- Vue CLI:默认的
tsconfig.json已经包含 paths 配置 - Vite:
vite.config.ts的 alias 不会自动同步到 TypeScript,必须手动配置tsconfig.json
Vue 模板中使用 💎
Vite 默认支持在 JS / TS 中使用别名,Vue 模板中 也支持:
<template>
<!-- 组件引用 -->
<UserCard :user="user" />
<!-- 静态资源 -->
<img :src="logoUrl" />
</template>
<script setup lang="ts">
// ✅ 别名
import UserCard from '@/components/UserCard.vue'
import { formatDate } from '@/utils/date'
import logoUrl from '@/assets/logo.png'
// ❌ 不推荐
import UserCard from '../../components/UserCard.vue'
</script>自动解析扩展名 💎
Vite 默认会自动补全扩展名,无需写 .vue / .ts:
// 这两种写法都可以
import UserCard from '@/components/UserCard'
import UserCard from '@/components/UserCard.vue'
// 默认的解析顺序
resolve: {
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.vue'],
}可以修改默认顺序,让 .vue 优先:
export default defineConfig({
resolve: {
extensions: ['.vue', '.ts', '.js', '.json', '.mjs'],
},
})二、目录别名(高级) 💎
通过 alias 的数组形式,可以做更灵活的路径匹配:
多个别名映射同一目录 👻
import { defineConfig } from 'vite'
import path from 'node:path'
export default defineConfig({
resolve: {
alias: [
{ find: '@', replacement: path.resolve(__dirname, 'src') },
{ find: '~', replacement: path.resolve(__dirname, 'src') },
{ find: 'vue', replacement: 'vue/dist/vue.esm-bundler.js' },
],
},
})自定义匹配规则 👻
// 自定义匹配规则
alias: [
{
find: /^@components\/(.*)$/,
replacement: path.resolve(__dirname, 'src/components/$1'),
},
]强制文件路径 👻
// 把整个包替换为本地文件(用于调试某个包)
alias: [
{
find: /^some-package$/,
replacement: path.resolve(__dirname, 'src/local/some-package.ts'),
},
]三、开发服务器代理 💎
开发服务器代理(proxy)是最常用的 dev server 配置之一,让我们可以在本地开发时直接请求后端 API,避免 CORS 跨域问题。
基本用法 💎
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
// 当请求 /api 开头的路径时,转发到后端
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
// 重写路径:去掉 /api 前缀
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
})// 业务代码中
fetch('/api/users').then(/* ... */)
// 实际请求:fetch('http://localhost:3000/users')完整选项 💎
proxy: {
'/api': {
// 目标服务器
target: 'http://localhost:3000',
// 修改请求头 origin(解决跨域)
changeOrigin: true,
// 路径重写
rewrite: (path) => path.replace(/^\/api/, ''),
// 是否启用 HTTPS
secure: false,
// 是否启用 WebSocket 代理
ws: true,
// 自定义错误处理
configure: (proxy, options) => {
proxy.on('error', (err, req, res) => {
console.error('代理错误:', err)
res.status(500).end('Proxy Error')
})
},
// 路径过滤(自定义哪些请求走代理)
bypass: (req, res) => {
if (req.url?.includes('/no-proxy')) {
res.writeHead(302, { Location: '/' })
return res.end()
}
},
},
}多后端代理 💎
proxy: {
// 主 API
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
// 文件服务
'/uploads': {
target: 'http://localhost:3001',
changeOrigin: true,
},
// 第三方服务
'/baidu-api': {
target: 'https://api.baidu.com',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/baidu-api/, ''),
},
}根据环境动态代理 💎
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
server: {
proxy: {
'/api': {
// 根据环境变量决定代理目标
target: env.VITE_API_PROXY_TARGET || 'http://localhost:3000',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
},
},
}
})# .env.development
VITE_API_PROXY_TARGET=http://192.168.1.100:3000
# .env.staging
VITE_API_PROXY_TARGET=https://staging-api.example.com代理路径完全替换 💎
proxy: {
// 把 /legacy/api 完整转发到 https://old-server.com/api
'/legacy/api': {
target: 'https://old-server.com',
changeOrigin: true,
// 注意:这里不重写路径
},
}// 请求 /legacy/api/users -> https://old-server.com/legacy/api/users代理静态资源 💎
proxy: {
// 代理图片服务器
'/img': {
target: 'https://cdn.example.com',
changeOrigin: true,
},
}四、代理常见问题 💎
代理不生效 👻
- 确认 dev server 正在运行
- 检查请求路径是否匹配 proxy key
- 确认
target地址可访问 - 重启 dev server
跨域问题 👻
CORS 是浏览器同源策略导致的问题,通过 Vite 代理 可以完美绕过:
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true, // ✅ 必须设置
},
}
changeOrigin: true会把请求头的Host修改为target的域名,避免后端做域名校验。
WebSocket 代理失败 👻
proxy: {
'/socket.io': {
target: 'http://localhost:3000',
changeOrigin: true,
ws: true, // ✅ 必须开启
},
}代理超时 👻
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
timeout: 60000, // 60 秒
},
}五、代理 vs baseURL 💎
很多同学分不清代理和 baseURL 的关系:
| 特性 | 代理 (proxy) | baseURL |
|---|---|---|
| 作用 | dev server 中转请求 | 拼接请求 URL 前缀 |
| 生效环境 | 仅 dev / preview | 所有环境 |
| 解决问题 | 跨域、远程后端调试 | 多环境 URL 切换 |
最佳实践:
// 业务代码
const api = axios.create({
// 通过环境变量配置,dev / prod 都生效
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
})
// 浏览器实际请求:
// dev: localhost:5173/api/users -> 代理到 localhost:3000/users
// prod: https://api.example.com/api/users// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
},
},
})六、实战案例 💎
封装网络请求模块 👻
// src/utils/request.ts
import axios from 'axios'
import { env } from './env'
const request = axios.create({
baseURL: env.API_BASE_URL,
timeout: 10000,
})
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 统一注入 token
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
},
(error) => Promise.reject(error)
)
// 响应拦截器
request.interceptors.response.use(
(response) => response.data,
(error) => {
// 统一错误处理
if (error.response?.status === 401) {
// 跳转登录
}
return Promise.reject(error)
}
)
export default request// src/api/user.ts
import request from '@/utils/request'
export const getUserInfo = () => request.get('/user/info')
export const login = (data: { username: string; password: string }) =>
request.post('/user/login', data)<!-- 使用 -->
<script setup lang="ts">
import { getUserInfo, login } from '@/api/user'
const user = await getUserInfo()
const result = await login({ username: 'admin', password: '123456' })
</script>多服务代理 👻
proxy: {
// 主 API
'^/api/.*': {
target: 'http://localhost:3000',
changeOrigin: true,
},
// AI 服务
'^/ai/.*': {
target: 'http://localhost:5000',
changeOrigin: true,
},
// 静态资源
'^/static/.*': {
target: 'http://localhost:3001',
changeOrigin: true,
},
}常见问题 💎
alias 在 SCSS 中不生效 👻
CSS / SCSS 中默认不支持 ~ 之类的别名,但 Vite 会处理 ~ 和 @ 形式的路径:
// ✅ 可以
@import '@/styles/variables.scss';
// ❌ 不可以
@import 'src/styles/variables.scss';alias 不支持 👻
- 重启 dev server
- 检查路径是否正确
- 确认
tsconfig.json的 paths 也配置了
proxy 报错 ECONNREFUSED 👻
后端服务没有启动,或者 target 端口错误。
小结
本章我们学习了 Vite 中的路径别名与代理配置:
- 路径别名:使用
@/简化路径,配合tsconfig.json获得类型提示 - 目录别名高级用法:正则匹配、自定义规则
- 开发代理:解决跨域、支持 WebSocket
- 多环境代理:根据环境变量动态切换
- 代理 vs baseURL:dev 代理 + 全局 baseURL 是最佳实践
下一章我们将学习 常用插件推荐。
至此,本章节的学习就到此结束了,如有疑惑,可对接技术客服进行相关咨询。