35、Vue项目本地开发完成后部署到服务器后报404是什么原因呢?
目录
一、路由模式导致的 404(最常见)
问题原因
两种模式对比
解决方案
二、资源路径配置错误(第二大元凶)
问题表现
常见场景
解决方案
同步配置 Router 的 base
三、服务器配置问题
Nginx 配置(最常用)
Apache 配置 (.htaccess)
Node.js Express 配置
Tomcat 配置 (web.xml)
Caddy 配置
四、构建与部署问题
常见问题清单
构建检查脚本
部署后验证
五、环境变量配置错误
问题场景
正确配置方式
使用方式
注意事项
六、Vue Router 配置问题
路由懒加载错误
404 页面配置
路由守卫问题
七、排查步骤清单(按顺序检查)
快速诊断命令
八、不同部署场景的完整配置示例
场景一:Nginx + History 模式 + 根目录部署
场景二:Nginx + History 模式 + 子目录部署
场景三:Docker + Nginx 部署
九、面试怎么回答才精彩
回答模板(分层递进)
加分回答要点
总结
一、路由模式导致的 404(最常见)
问题原因
Vue Router 的 history 模式使用 HTML5 History API,URL 看起来像正常路径(如 /user/profile),但服务器会尝试查找这个实际不存在的文件。
// router/index.js
const router = createRouter({
history: createWebHistory(), // history 模式 - 需要服务器配置
// history: createWebHashHistory(), // hash 模式 - 不需要额外配置
routes
})
两种模式对比
| 特性 | Hash 模式 | History 模式 |
|---|---|---|
| URL 样式 | example.com/#/user | example.com/user |
| 服务器配置 | 不需要 | 必须配置 |
| SEO | 较差 | 较好 |
| 美观度 | 有 # 号 | 更美观 |
解决方案
方案一:改用 Hash 模式(最简单)
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes
})
方案二:配置服务器回退(推荐)
见第三部分服务器配置。
二、资源路径配置错误(第二大元凶)
问题表现
- 页面空白,控制台报 JS/CSS 文件 404
- 资源请求路径错误(如请求
/assets/xxx变成/子目录/assets/xxx)
常见场景
| 部署位置 | 正确的 base 配置 |
|---|---|
域名根目录 example.com/ | / |
子目录 example.com/app/ | /app/ |
| 相对路径 | ./ |
解决方案
Vue CLI 项目 (vue.config.js)
module.exports = {
// 部署到子目录时
publicPath: process.env.NODE_ENV === 'production' ? '/your-sub-path/' : '/',
// 或者使用相对路径(适合不确定部署路径的情况)
// publicPath: './',
outputDir: 'dist',
assetsDir: 'static'
}
Vite 项目 (vite.config.js)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: '/your-sub-path/', // 部署到子目录
// base: './', // 相对路径
build: {
outDir: 'dist',
assetsDir: 'assets'
}
})
同步配置 Router 的 base
const router = createRouter({
history: createWebHistory('/your-sub-path/'), // 与 publicPath/base 保持一致
routes
})
三、服务器配置问题
Nginx 配置(最常用)
server {
listen 80;
server_name example.com;
root /usr/share/nginx/html;
index index.html;
# 核心配置:所有路由回退到 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 静态资源缓存
location ~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# API 代理(如果需要)
location /api/ {
proxy_pass http://backend-server:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
}
部署到子目录的 Nginx 配置
server {
listen 80;
server_name example.com;
# 子目录部署
location /app/ {
alias /usr/share/nginx/html/app/;
index index.html;
try_files $uri $uri/ /app/index.html; # 注意这里也要加子目录
}
}
Apache 配置 (.htaccess)
Options -MultiViews
RewriteEngine On
RewriteBase /
# 如果部署在子目录,改为:RewriteBase /your-sub-path/
RewriteRule ^index.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
Node.js Express 配置
const express = require('express')
const path = require('path')
const history = require('connect-history-api-fallback')
const app = express()
// 必须在静态文件中间件之前
app.use(history({
// 可选:指定哪些路径不需要回退
rewrites: [
{ from: /^/api/.*$/, to: context => context.parsedUrl.path }
]
}))
// 静态文件服务
app.use(express.static(path.join(__dirname, 'dist')))
app.listen(3000, () => {
console.log('Server running on port 3000')
})
Tomcat 配置 (web.xml)
404
/index.html
Caddy 配置
example.com {
root * /var/www/html
encode gzip
try_files {path} /index.html
file_server
}
四、构建与部署问题
常见问题清单
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 文件未上传完整 | FTP 中断/遗漏 | 检查 dist 目录完整性 |
| 旧缓存干扰 | 浏览器/CDN 缓存 | 清除缓存,添加版本号 |
| 构建环境不一致 | Node 版本差异 | 统一 Node 版本 |
| 文件权限问题 | Linux 权限不足 | chmod -R 755 dist |
构建检查脚本
#!/bin/bash
echo "=== 构建前检查 ==="
# 检查 Node 版本
echo "Node 版本: $(node -v)"
echo "npm 版本: $(npm -v)"
# 清理旧构建
rm -rf dist node_modules/.cache
# 安装依赖
npm ci
# 构建
npm run build
# 检查构建结果
echo "=== 构建结果检查 ==="
if [ -f "dist/index.html" ]; then
echo "✅ index.html 存在"
else
echo "❌ index.html 不存在"
exit 1
fi
# 检查资源文件
echo "JS 文件数量: $(find dist -name '*.js' | wc -l)"
echo "CSS 文件数量: $(find dist -name '*.css' | wc -l)"
echo "=== 构建完成 ==="
部署后验证
# 检查文件是否存在
curl -I https://example.com/
curl -I https://example.com/assets/index.xxx.js
# 检查路由是否正常回退
curl -I https://example.com/user/profile
# 应该返回 200,而不是 404
五、环境变量配置错误
问题场景
API 地址在本地和生产环境不同,导致请求失败。
正确配置方式
Vue CLI 项目
# .env.development
VUE_APP_API_BASE_URL=http://localhost:3000/api
# .env.production
VUE_APP_API_BASE_URL=https://api.example.com
Vite 项目
# .env.development
VITE_API_BASE_URL=http://localhost:3000/api
# .env.production
VITE_API_BASE_URL=https://api.example.com
使用方式
// Vue CLI
const apiUrl = process.env.VUE_APP_API_BASE_URL
// Vite
const apiUrl = import.meta.env.VITE_API_BASE_URL
注意事项
// ❌ 错误:硬编码地址
axios.defaults.baseURL = 'http://localhost:3000'
// ✅ 正确:使用环境变量
axios.defaults.baseURL = import.meta.env.VITE_API_BASE_URL
六、Vue Router 配置问题
路由懒加载错误
// ❌ 可能导致问题的写法
const routes = [
{
path: '/user',
component: () => import('@/views/User.vue') // 路径错误会导致 404
}
]
// ✅ 添加 webpackChunkName 便于调试
const routes = [
{
path: '/user',
component: () => import(/* webpackChunkName: "user" */ '@/views/User.vue')
}
]
404 页面配置
const routes = [
// ... 其他路由
// 放在最后,捕获所有未匹配的路由
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/NotFound.vue')
}
]
路由守卫问题
router.beforeEach((to, from, next) => {
// ❌ 错误:可能导致无限循环或意外跳转
if (!isAuthenticated) {
next('/login')
}
// ✅ 正确:添加条件判断
if (!isAuthenticated && to.path !== '/login') {
next('/login')
} else {
next()
}
})
七、排查步骤清单(按顺序检查)
┌─────────────────────────────────────────────────────────────┐
│ 404 问题排查流程 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1️⃣ 检查 dist/index.html 是否存在 │
│ └─ 不存在 → 重新构建 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2️⃣ 检查浏览器控制台 Network 面板 │
│ └─ 哪些资源 404?JS/CSS/API? │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3️⃣ 检查资源请求路径是否正确 │
│ └─ 路径错误 → 修改 publicPath/base 配置 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4️⃣ 直接访问首页是否正常? │
│ └─ 正常 → 路由问题,检查服务器 history 回退配置 │
│ └─ 不正常 → 服务器配置或文件部署问题 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5️⃣ 检查服务器配置 │
│ └─ Nginx: try_files 配置 │
│ └─ Apache: .htaccess 配置 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6️⃣ 清除缓存后重试 │
│ └─ 浏览器缓存 + CDN 缓存 │
└─────────────────────────────────────────────────────────────┘
快速诊断命令
# 1. 检查服务器文件
ls -la /path/to/dist/
# 2. 检查 Nginx 配置语法
nginx -t
# 3. 查看 Nginx 错误日志
tail -f /var/log/nginx/error.log
# 4. 测试路由回退
curl -I https://example.com/any-route
# 期望:HTTP 200,返回 index.html
八、不同部署场景的完整配置示例
场景一:Nginx + History 模式 + 根目录部署
vite.config.js
export default defineConfig({
base: '/',
plugins: [vue()]
})
router/index.js
const router = createRouter({
history: createWebHistory('/'),
routes
})
nginx.conf
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
场景二:Nginx + History 模式 + 子目录部署
vite.config.js
export default defineConfig({
base: '/admin/',
plugins: [vue()]
})
router/index.js
const router = createRouter({
history: createWebHistory('/admin/'),
routes
})
nginx.conf
location /admin/ {
alias /usr/share/nginx/html/admin/;
try_files $uri $uri/ /admin/index.html;
}
场景三:Docker + Nginx 部署
Dockerfile
# 构建阶段
FROM node:18-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;"]
nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
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";
}
}
九、面试怎么回答才精彩
回答模板(分层递进)
面试官:Vue 项目部署后出现 404,你会怎么排查?
参考回答:
"Vue 项目部署后 404 是一个常见问题,我会从三个层面来分析和解决:
第一层:路由模式问题(最常见)
如果是刷新页面或直接访问非首页路由时 404,基本可以确定是 History 模式的问题。
Vue Router 的 history 模式使用 HTML5 History API,URL 虽然美观,但服务器会把 /user/profile 当成真实路径去查找文件。
解决方案有两个:
- 简单方案:改用 hash 模式,URL 带
#号但不需要服务器配置 - 推荐方案:配置服务器回退,比如 Nginx 的
try_files $uri $uri/ /index.html
第二层:资源路径问题
如果首页能访问但页面空白,控制台显示 JS/CSS 文件 404,那就是 publicPath 配置错误。
特别是部署到子目录时,需要同时配置:
- 构建工具的
base或publicPath - Vue Router 的
base参数 - 服务器的
alias或root路径
三者必须保持一致。
第三层:服务器和部署问题
包括:
- 文件是否完整上传
- 文件权限是否正确
- 服务器配置语法是否正确
- 是否有缓存干扰
我的排查习惯是:先看浏览器 Network 面板确定是哪类资源 404,再针对性地检查配置。
在实际项目中,我会把部署配置标准化,比如用 Docker 统一环境,用 CI/CD 自动化部署,从源头减少这类问题。"
加分回答要点
| 维度 | 加分点 |
|---|---|
| 深度 | 能解释 History API 原理,为什么需要服务器配置 |
| 广度 | 了解不同服务器(Nginx/Apache/Node)的配置方式 |
| 实践 | 有实际踩坑和解决经验,能说出具体案例 |
| 工程化 | 提到 Docker、CI/CD、配置标准化等工程实践 |
| 思维 | 展示系统性的排查思路,而不是碰运气 |
总结
┌────────────────────────────────────────────────────────────┐
│ 404 问题核心要点 │
├────────────────────────────────────────────────────────────┤
│ 🔴 History 模式必须配置服务器回退 │
│ 🟡 publicPath/base 必须与部署路径匹配 │
│ 🟢 Router base 必须与 publicPath 一致 │
│ 🔵 服务器配置 try_files 是关键 │
│ ⚪ 排查时先看 Network 面板定位问题类型 │
└────────────────────────────────────────────────────────────┘










