与后端集成
本章涵盖 Vite 与主流后端(Node.js / Golang / Python 等)的集成方案,包括开发代理、生产部署和 SSR。
开发阶段:API 代理
解决前后端分离项目在开发阶段的跨域问题。
最简代理
// vite.config.js
export default defineConfig({
server: {
proxy: {
'/api': 'http://localhost:8080',
},
},
})前端请求 /api/users → 代理到 http://localhost:8080/api/users
路径重写
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''), // /api/users → /users
},
}多个后端服务
proxy: {
'/api': 'http://localhost:8080',
'/auth': 'http://localhost:8081',
'/ws': {
target: 'ws://localhost:8082',
ws: true,
},
}生产部署:静态文件 + 后端服务
构建产物是纯静态文件,可以部署到任何 Web 服务器或 CDN。
方式一:Nginx 反向代理
server {
listen 80;
server_name example.com;
root /var/www/dist;
index index.html;
# API 代理到后端
location /api/ {
proxy_pass http://backend:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# SPA 回退:所有非文件请求返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源强缓存(带 hash 的文件)
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}💡 最佳实践:带 hash 的构建产物可以设置
immutable强缓存,index.html必须设为no-cache(每次协商)。
方式二:Node.js 后端直接托管
// server.js
import express from 'express'
import { fileURLToPath } from 'url'
import path from 'path'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const app = express()
// 静态文件服务
app.use(express.static(path.join(__dirname, 'dist')))
// API 路由
app.get('/api/hello', (req, res) => {
res.json({ msg: 'Hello from backend' })
})
// SPA fallback
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(3000)方式三:Golang 嵌入静态文件
package main
import (
"embed"
"io/fs"
"net/http"
)
//go:embed dist/*
var dist embed.FS
func main() {
// 去除 dist/ 前缀
staticFS, _ := fs.Sub(dist, "dist")
mux := http.NewServeMux()
// API 路由
mux.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"msg": "Hello"}`))
})
// 静态文件 + SPA fallback
mux.Handle("/", spaHandler{fs: staticFS})
http.ListenAndServe(":8080", mux)
}中间件模式
当你的后端是 Node.js 且希望共用同一端口时,将 Vite 作为中间件引入:
// server.js
import express from 'express'
import { createServer as createViteServer } from 'vite'
async function createServer() {
const app = express()
// 1. 先创建 Vite 开发服务器(middlewareMode)
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
})
// 2. 将 Vite 中间件挂载到 Express
app.use(vite.middlewares)
// 3. 你的 API 路由
app.get('/api/data', async (req, res) => {
res.json({ data: 'hello' })
})
// 4. 这步可选——如果要用 Vite 的 HTML 处理
app.use('*', async (req, res, next) => {
const url = req.originalUrl
try {
let template = fs.readFileSync('index.html', 'utf-8')
template = await vite.transformIndexHtml(url, template)
res.status(200).set({ 'Content-Type': 'text/html' }).end(template)
} catch (e) {
vite.ssrFixStacktrace(e)
next(e)
}
})
app.listen(3000)
}
createServer()💡 最佳实践:中间件模式也适用于测试框架(Vitest 的
pool: 'forks')和一些自定义 dev server 需求。
SSR(服务端渲染)
Vite 对 SSR 提供了一等公民的支持。
基础 SSR 架构
src/
├── entry-server.js # 服务端入口
├── entry-client.js # 客户端入口
├── App.vue(或 App.jsx)
└── router/服务端入口
// src/entry-server.js
export async function render(url) {
// 根据 URL 匹配路由、拉取数据、渲染为 HTML 字符串
const html = renderToString(/* ... */)
return { html, state: {} }
}客户端入口
// src/entry-client.js
import { createApp } from './app'
const { app, router } = createApp()
router.isReady().then(() => {
app.mount('#app')
})SSR 服务器
// server.js
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import express from 'express'
import { createServer as createViteServer } from 'vite'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function createServer() {
const app = express()
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
try {
const url = req.originalUrl
// 1. 读取 index.html
let template = fs.readFileSync(
path.resolve(__dirname, 'index.html'),
'utf-8'
)
// 2. 应用 Vite HTML 转换(注入 HMR 客户端等)
template = await vite.transformIndexHtml(url, template)
// 3. 加载服务端入口(Vite 实时编译)
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
// 4. 渲染
const { html, state } = await render(url)
// 5. 注入渲染结果到 HTML 模板
const responseHtml = template
.replace('<!--ssr-outlet-->', html)
.replace('<!--ssr-state-->', JSON.stringify(state))
res.status(200).set({ 'Content-Type': 'text/html' }).end(responseHtml)
} catch (e) {
vite.ssrFixStacktrace(e)
console.error(e)
res.status(500).end(e.stack)
}
})
app.listen(3000)
}
createServer()💡 最佳实践:生产环境的 SSR 不需要 Vite 中间件,直接使用构建产物中的
entry-server.js文件即可。例如import { render } from './dist/server/entry-server.js'。
部署配置
子路径部署(base)
当应用部署在非根路径(如 https://example.com/my-app/):
export default defineConfig({
base: '/my-app/',
})# 也可通过命令行指定
vite build --base=/my-app/base 影响:
- 资源 URL 前缀
<link>/<script>的href/srcimport.meta.env.BASE_URL
🚨 陷阱:
base末尾的/很重要。/my-app≠/my-app/——缺少末尾斜杠可能导致资源路径错误。
CDN 部署
export default defineConfig({
base: 'https://cdn.example.com/my-app/',
})部署平台适配
| 平台 | 关键配置 |
|---|---|
| Vercel | 无需配置,自动检测 Vite |
| Netlify | 构建命令 vite build,发布目录 dist,SPA 需 _redirects 文件 |
| Cloudflare Pages | 构建命令 vite build,输出 dist |
| GitHub Pages | base: '/repo-name/' + GitHub Actions |
| Nginx | 见上方 Nginx 配置 |
| Docker | 多阶段构建:先 npm run build,再 nginx:alpine 拷贝 dist/ |
Docker 部署示例
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]