凌峰创科服务平台

React Router 服务器端如何配置渲染?

React Router 本身是一个纯前端的库,它不直接“是”一个服务器。 要让 React Router 在现代 Web 应用中正常工作,特别是对于支持浏览器 URL(如 http://example.com/dashboard)的场景,服务器必须进行相应的配置和配合

React Router 服务器端如何配置渲染?-图1
(图片来源网络,侵删)

这种配合的核心是处理 “客户端路由”(Client-Side Routing)“服务端渲染”(Server-Side Rendering, SSR)


核心概念:客户端路由 vs. 服务端路由

在理解服务器如何配合之前,我们先要明白 React Router 解决的是什么问题。

传统服务端路由 (传统多页应用 - MPA)

  • 工作方式:用户在浏览器中输入一个 URL (如 http://example.com/products),浏览器向服务器发送请求,服务器收到请求后,根据 URL 路径 (/products),找到对应的 HTML 文件,然后把这个完整的 HTML 文件返回给浏览器。
  • 特点:每次 URL 变化,都会有一次完整的页面刷新,服务器负责“路由”和“渲染”。
  • 缺点:用户体验有卡顿,每次交互都需要重新加载整个页面。

客户端路由 (现代单页应用 - SPA,React Router 属于此类)

  • 工作方式:用户第一次访问应用时,服务器只返回一个最小的、包含所有 JavaScript 代码的 HTML 框架页面,之后,所有的路由切换(如从 /home 切换到 /about)都由浏览器内的 JavaScript 代码(React Router)来处理,它只更新页面中需要变化的部分,而不会刷新整个页面。
  • 特点:用户体验流畅,像桌面应用一样。
  • 问题:当用户直接在浏览器地址栏输入一个深层路径(如 http://example.com/dashboard/settings)或刷新一个非首页的 URL 时,如果服务器没有收到过对这个路径的请求,它会认为这个路径不存在,从而返回一个 404 Not Found 错误,因为服务器没有为 /dashboard/settings 这个路径准备一个 HTML 文件。

服务器的核心任务:处理客户端路由

为了解决上述问题,服务器需要扮演一个“智能中间人”的角色,无论用户请求的是 , /home, /dashboard, 还是 /dashboard/settings,服务器都应该做同一件事:返回应用的入口 HTML 文件(通常是 index.html)。

这个行为在技术上被称为 “回退”(Fallback)“所有路由都指向 index.html”

React Router 服务器端如何配置渲染?-图2
(图片来源网络,侵删)

不同类型服务器的配置示例:

Node.js (Express)

这是最常见的场景,通常与 React Router 搭配使用。

// server.js
const express = require('express');
const path = require('path');
const app = express();
// 1. 静态文件服务 (如 public 目录下的 CSS, JS, 图片等)
app.use(express.static(path.join(__dirname, 'build')));
// 2. 所有其他请求都回退到 index.html
// 这是处理客户端路由的关键!
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

说明

  • express.static 用来提供生产环境打包后的静态资源。
  • app.get('*', ...) 是一个通配符路由,它捕获所有没有被静态文件中间件处理的 GET 请求(也就是所有 API 请求和页面路由请求),然后将 index.html 发送给浏览器,React Router 会在浏览器端接管这个 URL 并渲染正确的组件。

Nginx

Nginx 是一个高性能的 Web 服务器和反向代理。

# /etc/nginx/sites-available/your-app
server {
    listen 80;
    server_name your-domain.com;
    # 指向你的 React 应用构建目录
    root /var/www/your-app/build;
    index index.html;
    # 处理静态资源,并设置缓存
    location /static/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
    # 这是处理客户端路由的关键!
    # 将所有非静态资源的请求都回退到 index.html
    location / {
        try_files $uri /index.html;
    }
    # (可选) API 请求可以代理到后端服务
    # location /api/ {
    #     proxy_pass http://backend-server;
    # }
}

说明

React Router 服务器端如何配置渲染?-图3
(图片来源网络,侵删)
  • rootindex 指定了 Nginx 应该从哪里寻找文件。
  • location /static/ 专门处理打包后的静态资源,提高性能。
  • location / { try_files $uri /index.html; } 是核心,它首先尝试请求用户访问的 URI ($uri),如果文件不存在(比如是 /dashboard),它就回退到 /index.html

Apache

Apache 的配置也类似。

# .htaccess 文件 (放在你的 build 目录下)
# 启用重写模块
RewriteEngine On
# 如果请求的不是真实存在的文件或目录,则重写到 index.html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

说明

  • 这个 .htaccess 文件利用了 Apache 的 mod_rewrite 模块。
  • RewriteCond 检查请求的路径是否不是一个文件 (!-f) 也不是一个目录 (!-d)。
  • 如果条件满足,RewriteRule 就将所有请求都重定向到 index.html

高级场景:服务端渲染 与 React Router

虽然上述“回退”方案能完美解决 SPA 的问题,但它有一个缺点:首屏加载速度和 SEO 可能不是最优的,因为浏览器需要先下载和解析 JavaScript,然后才能渲染出页面内容。

为了解决这个问题,我们可以采用 服务端渲染,在这种模式下,服务器会预先为用户请求的 URL 渲染出完整的 HTML 字符串,再发送给浏览器。

这时,服务器不仅需要“回退”,还需要一个“匹配器”(Matcher)

SSR 服务器的核心任务:

  1. 静态资源服务:和之前一样,提供 CSS, JS 等文件。
  2. API 路由服务:如果应用有后端 API,服务器需要处理这些请求。
  3. 页面路由服务:这是 SSR 的关键。
    • 当收到一个请求(如 /dashboard)时,服务器不能直接回退到 index.html
    • 服务器需要运行一套与前端 完全相同 的 React Router 逻辑。
    • 它根据请求的 URL (/dashboard),找到对应的 React 组件。
    • 在服务器上执行这个组件,将其渲染成 HTML 字符串。
    • 将这个 HTML 字符串包裹在一个完整的 HTML 页面模板中,发送给浏览器。
  4. 回退处理:如果服务器找不到匹配的路由(比如用户访问了一个不存在的 /foo-bar),则返回 404 页面。

SSR 服务器架构示例 (使用 Express + react-router-dom)

// server.js (SSR 版本)
const express = require('express');
const React = require('react');
const { renderToString } = require('react-dom/server');
const { StaticRouter } = require('react-router-dom/server'); // 注意是 server 版本
const App = require('./src/App').default; // 你的根组件
const app = express();
// 静态资源服务
app.use(express.static('build'));
// 页面路由服务
app.get('*', (req, res) => {
  // 1. 使用 StaticRouter 替换 BrowserRouter
  // context 对象用于在渲染过程中传递数据,比如处理 <Link> 的重定向
  const context = {};
  const html = renderToString(
    <StaticRouter location={req.url} context={context}>
      <App />
    </StaticRouter>
  );
  // 2. 将渲染的 HTML 插入到模板中
  const template = `
    <!DOCTYPE html>
    <html>
      <head>
        <title>SSR React App</title>
      </head>
      <body>
        <div id="root">${html}</div>
        <script src="/bundle.js"></script> <!-- 注入客户端 JS -->
      </body>
    </html>
  `;
  // 3. 处理重定向(context 中有)
  if (context.url) {
    return res.redirect(301, context.url);
  }
  // 4. 发送最终 HTML
  res.send(template);
});
app.listen(3000);

说明

  • StaticRouterreact-router-dom 专门为 SSR 提供的组件,它接收一个 location 属性(即服务器收到的 URL)。
  • renderToString 将 React 组件渲染成 HTML 字符串。
  • 服务器生成一个完整的 HTML 页面,并将渲染好的内容注入进去。
  • 它仍然会注入客户端的 JavaScript bundle (bundle.js),这样,当浏览器加载完 HTML 后,客户端的 React Router 会“接管”页面,后续的导航就变成了客户端路由,无需再请求服务器。

场景 服务器角色 核心配置/逻辑 适用情况
纯客户端渲染 智能回退服务器 所有未知路由 () 都返回 index.html 简单的 SPA,对 SEO 和首屏加载速度要求不高的项目。
服务端渲染 路由匹配器 + 回退服务器 为已知 URL 渲染对应的 HTML。
2. 为未知 URL 返回 404 或回退到 index.html
对 SEO 和首屏性能要求高的项目,如电商、博客、企业官网。
静态站点生成 静态文件服务器 直接部署构建出的 build 目录到任何静态托管服务(如 Netlify, Vercel, GitHub Pages)。 内容不经常变化的博客、文档、营销网站。

当你谈论“React Router 服务器”时,你实际上是在谈论一个能够正确处理 React Router 所需路由逻辑的 Web 服务器,对于绝大多数现代 React 应用,最基础和通用的要求就是配置服务器对所有路由进行回退,如果你的项目更高级,采用了 SSR,那么服务器就需要更复杂的路由匹配和渲染逻辑。

分享:
扫描分享到社交APP
上一篇
下一篇