1. 为什么需要特殊配置 Nginx 支持 Vue 路由
当我们在 Vue 项目中启用 history 模式时,实际上是在使用浏览器提供的 History API 来管理应用的路由状态。这种模式下,URL 看起来更加干净,没有传统单页应用中常见的 # 符号。比如,一个用户详情页的 URL 可能是 https://example.com/user/123,而不是 https://example.com/#/user/123。
然而,这种"漂亮"的 URL 也带来了一个技术挑战:当用户直接在浏览器地址栏输入 https://example.com/user/123 并回车时,浏览器会向服务器发送一个对这个特定路径的请求。如果服务器没有正确配置,它会尝试在服务器文件系统中查找 /user/123 这个文件或目录,显然找不到,于是返回 404 错误。
1.1 理解前端路由与服务器路由的区别
前端路由(如 Vue Router)和后端路由(如 Nginx 的路由)工作在不同的层面:
- 前端路由:运行在浏览器中,通过 JavaScript 动态渲染不同组件,不涉及实际的服务器请求
- 后端路由:服务器根据请求路径返回不同的资源或执行不同的处理逻辑
在传统的多页应用中,每个 URL 路径通常对应服务器上的一个实际文件。但在单页应用(SPA)中,我们只有一个入口文件(通常是 index.html),所有路由逻辑都由前端 JavaScript 处理。
1.2 history 模式与 hash 模式的对比
Vue Router 支持两种模式:
-
hash 模式(默认):
- 使用 URL 的 hash 部分(
#后面的内容)来模拟完整 URL - 例如:
http://example.com/#/user/123 - 优点:兼容性好,不需要服务器特殊配置
- 缺点:URL 不够美观,SEO 支持有限
- 使用 URL 的 hash 部分(
-
history 模式:
- 使用完整的 URL,没有
# - 例如:
http://example.com/user/123 - 优点:URL 干净美观,SEO 更友好
- 缺点:需要服务器配合配置
- 使用完整的 URL,没有
2. 完整配置流程详解
2.1 Vue 项目端配置
首先,我们需要确保 Vue 项目正确配置了 history 模式。在 Vue Router 4.x 中,配置方式如下:
javascript复制// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(), // 关键配置:使用 history 模式
routes: [
// 你的路由配置
{ path: '/', component: Home },
{ path: '/user/:id', component: User },
// 其他路由...
]
})
注意:如果你使用的是 Vue 2.x 和 Vue Router 3.x,配置方式略有不同:
javascript复制const router = new VueRouter({ mode: 'history', // 而不是 createWebHistory() routes: [...] })
2.2 构建 Vue 项目
配置好路由后,我们需要构建 Vue 项目以生成生产环境文件:
bash复制npm run build
构建完成后,你会在项目目录下看到一个 dist 文件夹,里面包含了所有静态文件:
code复制dist/
├── assets/
│ ├── index.123abc.js
│ └── style.456def.css
├── favicon.ico
└── index.html
2.3 Nginx 服务器配置
这是最关键的步骤。我们需要配置 Nginx,让它正确处理前端路由的请求。以下是完整的 Nginx 配置示例:
nginx复制server {
listen 80;
server_name example.com www.example.com; # 替换为你的域名
# 指定 Vue 项目的构建输出目录
root /var/www/vue-project/dist;
index index.html;
# 主路由配置 - 处理所有非静态资源请求
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
# 防止缓存过期的资源被错误地重新请求
add_header ETag "";
if_modified_since off;
}
# 错误页面配置(可选)
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# API 代理配置(如果你的后端 API 需要)
location /api/ {
proxy_pass http://backend-server:3000; # 替换为你的后端地址
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
2.3.1 关键配置解析
-
try_files $uri $uri/ /index.html:- 这是支持 Vue history 模式的核心指令
- 它的工作方式是:
- 首先尝试查找与请求 URI 完全匹配的文件(
$uri) - 如果找不到,尝试查找以该 URI 命名的目录(
$uri/) - 如果都找不到,最后返回
/index.html,由前端路由处理
- 首先尝试查找与请求 URI 完全匹配的文件(
-
静态资源缓存:
- 我们为静态资源设置了长期缓存(1年)
immutable告诉浏览器这些文件永远不会改变,可以永久缓存- 移除
ETag和If-Modified-Since头,避免不必要的验证请求
-
API 代理:
- 如果你的前端需要访问后端 API,且 API 位于不同域名或端口
- 通过 Nginx 代理可以避免跨域问题
2.4 测试并重启 Nginx
配置完成后,我们需要测试并应用这些更改:
bash复制# 测试配置文件语法是否正确
sudo nginx -t
# 如果测试通过,重启 Nginx
sudo systemctl restart nginx
3. 高级配置与优化
3.1 处理子目录部署
有时我们需要将 Vue 应用部署在域名的子目录下,例如 https://example.com/app/。这时需要做一些额外配置:
Vue 项目端:
-
在
vite.config.js或vue.config.js中设置base:javascript复制// vite.config.js export default defineConfig({ base: '/app/', // 关键配置 // 其他配置... }) -
在 Vue Router 中设置 base:
javascript复制const router = createRouter({ history: createWebHistory('/app/'), // 注意这里的 base // 其他配置... })
Nginx 配置:
nginx复制server {
# ...其他配置...
location /app/ {
alias /var/www/vue-project/dist/;
try_files $uri $uri/ /app/index.html;
}
}
3.2 性能优化配置
-
开启 gzip 压缩:
nginx复制gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1000; gzip_proxied any; -
HTTP/2 支持:
nginx复制listen 443 ssl http2; # 需要 SSL 证书 -
Brotli 压缩(需要额外模块):
nginx复制brotli on; brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
3.3 安全加固配置
-
禁用不必要的 HTTP 方法:
nginx复制if ($request_method !~ ^(GET|HEAD|POST)$ ) { return 405; } -
防止点击劫持:
nginx复制add_header X-Frame-Options "SAMEORIGIN"; -
内容安全策略(CSP):
nginx复制add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self';";
4. 常见问题排查与解决方案
4.1 访问路由返回 404 错误
可能原因:
- Nginx 的
try_files指令配置不正确 root路径没有指向正确的dist目录- Vue 项目的
publicPath配置不正确
解决方案:
- 确认 Nginx 配置中有
try_files $uri $uri/ /index.html - 检查
root路径是否正确指向 Vue 构建后的dist目录 - 确保 Vue 项目的
vite.config.js或vue.config.js中设置了正确的base
4.2 静态资源加载失败
可能原因:
- 资源路径不正确
- 缓存配置有问题
- 文件权限问题
解决方案:
- 检查浏览器开发者工具中的 Network 面板,确认资源请求路径
- 确保 Nginx 的静态资源 location 块配置正确
- 检查
dist目录及其内容的权限:bash复制sudo chown -R www-data:www-data /var/www/vue-project/dist sudo chmod -R 755 /var/www/vue-project/dist
4.3 路由跳转后页面空白
可能原因:
- Vue Router 配置问题
- 资源路径错误
- 浏览器缓存了旧版本
解决方案:
- 检查 Vue Router 的 base 配置是否与部署环境匹配
- 确保所有资源路径正确
- 尝试强制刷新浏览器(Ctrl+F5 或 Cmd+Shift+R)
4.4 API 代理不工作
可能原因:
- 代理路径配置错误
- 后端服务未运行
- 跨域问题
解决方案:
- 检查 Nginx 的
proxy_pass配置是否正确 - 确认后端服务正在运行并可访问
- 检查后端服务是否配置了正确的 CORS 头
5. 实际部署经验分享
在实际生产环境中部署 Vue 项目时,我总结了一些有价值的经验:
-
部署前测试:
- 在本地使用
nginx -t测试配置 - 使用 Docker 或虚拟机搭建与生产环境相似的测试环境
- 在本地使用
-
渐进式部署:
- 先部署到测试环境验证
- 使用蓝绿部署或金丝雀发布策略减少风险
-
监控与日志:
- 配置 Nginx 访问日志和错误日志
- 设置监控告警,及时发现 404 或其他错误
-
回滚策略:
- 保留旧版本的构建产物
- 准备快速回滚的脚本或方案
-
性能考量:
- 对于大型应用,考虑代码分割和懒加载
- 使用 CDN 分发静态资源
-
SEO 优化:
- 对于需要 SEO 的页面,考虑预渲染或服务端渲染(SSR)
- 确保关键元信息正确设置
-
安全实践:
- 定期更新 Nginx 和系统软件
- 限制敏感目录的访问
- 使用 HTTPS 并配置安全头
通过以上配置和经验,你应该能够顺利地在 Nginx 上部署 Vue 项目,并完美支持 history 模式的路由。如果在实施过程中遇到任何问题,建议先检查 Nginx 错误日志(通常在 /var/log/nginx/error.log),它通常会提供有价值的调试信息。