前言
- 当我们开发好的SPA前端项目打包部署到nginx等web容器中后,遇到了404的路由访问问题,比如:当我访问 "http://192.168.1.102:8080/" 是能正常访问页面的,但是当我直接通过 "http://192.168.1.102:8080/cat" 就会出现404了
- 所以,这是什么原因呢?
原因说明
- React 项目路由注册案例
tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import './App.css'
import RootLayout from './pages/RootLayout'
import Cat from './pages/Cat'
import Dog from './pages/Dog'
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<RootLayout />}>
<Route index element={<Navigate to='/cat' replace />} />
<Route path='cat' element={<Cat />} />
<Route path='dog' element={<Dog />} />
</Route>
</Routes>
</BrowserRouter>
)
}
export default App
问题原因
- React Router 的客户端路由原理
React Router 使用的是客户端路由 ,它依赖于 JavaScript 在浏览器端动态渲染页面。当你访问 http://localhost:8080/ 时,浏览器会加载你的应用的 index.html,然后 React Router 根据当前的 URL 动态渲染对应的组件(例如 MyDay 组件)。在这种情况下,Nginx 只需要正确返回 index.html,React Router 就会接管路由。 - 直接访问子路由(如 /cat)时的行为
当你直接访问 http://localhost:8080/cat 时,浏览器会向 Nginx 发送一个请求,试图获取 /cat 对应的资源。然而,Nginx 并不知道 /cat 是一个客户端路由,它会尝试在文件系统中寻找 /cat 对应的静态文件(例如 /cat/index.html)。由于你的 React 项目是单页应用(SPA),并没有 /cat 这样的静态文件,因此 Nginx 返回了 404 错误。 - 为什么访问根路径 / 没有问题?
当你访问 http://localhost:8080/ 时,Nginx 会正确返回 index.html,然后 React Router 在浏览器端接管路由,并根据路由配置渲染正确的页面。
你的问题是由于 React Router 和 Nginx 的路由处理方式不匹配导致的。下面我会详细解释问题的原因,并提供解决方案。
解决方案
为了解决这个问题,你需要在 Nginx 的配置中确保所有未知的路由请求(例如 /cat
)都返回 index.html
,从而让 React Router 在浏览器端接管路由。以下是详细的解决方案:
方案 1:在 Nginx 中配置路由重定向
你需要修改 Nginx 的配置文件,添加一个规则,确保所有未匹配的路径都返回 index.html
。假设你的 React 项目构建后的文件存放在 /usr/share/nginx/html
目录下(或者你自己的部署目录),可以按以下方式配置:
- 打开 Nginx 配置文件(通常位于
/etc/nginx/nginx.conf
或/etc/nginx/conf.d/
目录下)。 - 添加或修改
server
配置块如下:
nginx
server {
listen 8080;
server_name localhost;
root /usr/share/nginx/html; # 替换为你的 React 项目构建文件的实际路径
index index.html;
location / {
try_files $uri $uri/ /index.html; # 核心配置
}
}
解释:
try_files $uri $uri/ /index.html;
是关键配置。它的意思是:- 首先尝试查找请求的
$uri
(例如/cat
)。 - 如果找不到,则尝试查找
$uri/
(例如/cat/
)。 - 如果仍然找不到,则将请求重定向到
/index.html
,由 React Router 接管路由。
- 首先尝试查找请求的
- 保存配置文件后,重新加载 Nginx 配置以应用更改:
bash
sudo nginx -t # 检查配置文件语法是否正确
sudo systemctl reload nginx # 重新加载 Nginx
- 现在直接访问
http://localhost:8080/cat
应该可以正常加载页面。
方案 2:使用 HashRouter 替代 BrowserRouter
如果你无法修改 Nginx 配置(例如在某些托管环境中),可以考虑在 React 项目中使用 HashRouter
替代 BrowserRouter
。HashRouter
使用 URL 的哈希部分(例如 http://localhost:8080/#/cat
)来管理路由,这种方式不需要服务器端的支持。
-
修改你的 React 代码,将
BrowserRouter
替换为HashRouter
: -
重新打包部署你的项目。
优点:
- 不需要修改服务器配置,适合无法控制服务器环境的情况。
缺点:
- URL 中会多出一个
#
符号(例如http://localhost:8080/#/cat
),可能不符合 SEO 要求或美观需求。
方案 3:使用其他服务器配置(如果不使用 Nginx)
如果你使用的不是 Nginx,而是其他服务器(如 Apache 或 Express),也需要类似的配置:
-
Apache 配置:
在
.htaccess
文件中添加以下内容:apacheRewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]
-
Express.js 配置:
如果使用 Node.js 的 Express 服务器,确保所有未匹配的路由都返回
index.html
:javascriptconst express = require('express'); const path = require('path'); const app = express(); app.use(express.static(path.join(__dirname, 'build'))); app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'build', 'index.html')); }); app.listen(8080);
推荐方案
- 如果你有权限修改服务器配置,推荐使用 方案 1(Nginx 配置),因为这是最常见且最优雅的解决方案,适用于绝大多数单页应用(SPA)。
- 如果你无法修改服务器配置,或者只是临时测试,推荐使用 方案 2(HashRouter)。
测试验证
-
部署完成后,尝试以下操作:
- 直接访问
http://localhost:8080/
,验证是否正常跳转到/cat
。 - 直接访问
http://localhost:8080/cat
,验证是否能正确加载页面。 - 直接访问
http://localhost:8080/dog
,验证是否能正确加载页面。
- 直接访问
-
如果仍有问题,可以检查以下几点:
- 确认 Nginx 配置是否正确应用(可以用
nginx -T
查看当前生效的配置)。 - 确认 React 项目的构建文件是否正确部署到 Nginx 的
root
目录下。 - 查看浏览器开发者工具中的网络请求,确认是否返回了 404 或其他错误。
- 确认 Nginx 配置是否正确应用(可以用
总结
你的问题是典型的单页应用(SPA)部署问题,核心原因是服务器端没有正确处理客户端路由。通过在 Nginx 中配置 try_files
规则,可以确保所有路由请求都返回 index.html
,从而让 React Router 接管路由。如果无法修改服务器配置,可以使用 HashRouter
作为替代方案。