Vue单页应用使用createWebHistory路由模式时,刷新页面会出现404错误。这是因为:
- 用户访问
/apps/drplayer/settings - 浏览器向服务器请求
/apps/drplayer/settings文件 - 服务器找不到该文件,返回404
- 需要让服务器将所有SPA路由请求都返回到
index.html
通过Fastify提供静态文件服务 + SPA路由回退机制,完全可以解决刷新404问题。
# 方法1:使用构建脚本(推荐)
node build-for-fastify.js
# 方法2:手动设置环境变量
set VITE_BASE_PATH=/apps/drplayer/
pnpm build将 dist/ 目录的所有内容复制到您的后端 apps/drplayer/ 目录:
your-backend/
├── apps/
│ └── drplayer/
│ ├── index.html
│ ├── assets/
│ │ ├── index-xxx.js
│ │ └── index-xxx.css
│ └── ...
└── server.js
import { addSPARoutes } from './fastify-spa-routes.js';
import fastifyStatic from '@fastify/static';
// 您现有的静态文件配置
await fastify.register(fastifyStatic, {
root: options.appsDir,
prefix: '/apps/',
decorateReply: false,
});
// 添加SPA路由支持
await fastify.register(addSPARoutes, {
appsDir: options.appsDir
});import path from 'path';
import fs from 'fs';
// 在您现有的Fastify应用中添加这些路由
fastify.get('/apps/drplayer/*', async (request, reply) => {
const requestedPath = request.params['*'];
const fullPath = path.join(options.appsDir, 'drplayer', requestedPath);
try {
await fs.promises.access(fullPath);
return reply.callNotFound(); // 让静态文件服务处理
} catch (error) {
// 返回index.html让Vue Router处理
const indexPath = path.join(options.appsDir, 'drplayer', 'index.html');
const indexContent = await fs.promises.readFile(indexPath, 'utf8');
return reply
.type('text/html')
.header('Cache-Control', 'no-cache, no-store, must-revalidate')
.send(indexContent);
}
});
// 处理根路径
fastify.get('/apps/drplayer', async (request, reply) => {
return reply.redirect(301, '/apps/drplayer/');
});
fastify.get('/apps/drplayer/', async (request, reply) => {
const indexPath = path.join(options.appsDir, 'drplayer', 'index.html');
const indexContent = await fs.promises.readFile(indexPath, 'utf8');
return reply
.type('text/html')
.header('Cache-Control', 'no-cache, no-store, must-revalidate')
.send(indexContent);
});- 静态文件服务:
@fastify/static处理所有存在的静态文件(JS、CSS、图片等) - 路由回退:当请求的文件不存在时,返回
index.html - Vue Router接管:
index.html加载后,Vue Router根据URL显示对应组件
请求: /apps/drplayer/assets/index-xxx.js
↓
静态文件存在 → 直接返回文件
请求: /apps/drplayer/settings
↓
静态文件不存在 → 返回 index.html → Vue Router处理
- HTML文件:不缓存(
no-cache),确保路由更新 - 静态资源:长期缓存(1年),提高性能
- 启动Fastify服务器
- 访问
http://localhost:3000/apps/drplayer/ - 导航到不同页面(如设置页面)
- 刷新页面,确认不出现404错误
- 检查浏览器开发者工具,确认静态资源正常加载
A: 检查 VITE_BASE_PATH 是否正确设置为 /apps/drplayer/
A: 确认构建时的base路径与Fastify的prefix匹配
A: 确保API路由在SPA路由之前注册,避免被SPA回退拦截
- 启用Gzip压缩:
import fastifyCompress from '@fastify/compress';
await fastify.register(fastifyCompress);- 设置静态资源缓存:
await fastify.register(fastifyStatic, {
// ... 其他配置
setHeaders: (res, path) => {
if (path.endsWith('.html')) {
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
} else if (path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/)) {
res.setHeader('Cache-Control', 'public, max-age=31536000');
}
}
});✅ 可以解决404问题:通过Fastify的路由回退机制
✅ 性能良好:静态文件直接服务,只有路由请求才回退
✅ 配置简单:只需添加几个路由处理器
✅ 开发友好:支持热重载和开发服务器