使用React Router 7.5进行静态站点生成(SSG)教程

关键要点

  • React Router v7.5.0支持通过Vite插件的prerender配置实现SSG(静态站点生成),适合静态部署。
  • 建议设置ssr: false以确保运行时无服务器渲染,并通过prerender指定需要生成的静态路径。
  • 构建时会生成HTML和数据文件,可部署到Netlify或GitHub Pages等静态服务器。
  • 对于动态路由,需要在prerender函数中明确列出所有路径,例如从CMS获取。

概述

React Router v7.5.0的发布带来了对静态站点生成(SSG)的增强支持,特别是在Vite插件中引入了prerender配置。这一功能允许开发者在构建时生成静态HTML和数据文件,适合部署到静态文件服务器(如Netlify、GitHub Pages或Vercel),从而提高性能和SEO效果。以下是基于最新版本的详细SSG方案,包括配置、实现步骤和注意事项。

React Router v7.5.0于2025年4月4日发布,但对SSG的支持在v7.0.0中已引入,并延续到v7.5.0。生成的文件可以静态服务,适合不需要运行时服务器渲染的场景。我个人使用体验感觉优于Nextjs。

准备工作

首先,确保安装了必要的依赖:

shell 复制代码
npm install [email protected] @react-router/[email protected] @react-router/[email protected]
# 或使用pnpm
pnpm add [email protected] @react-router/[email protected] @react-router/[email protected]

项目结构设置

一个典型的使用React Router 7.5的SSG项目结构如下:

shell 复制代码
/src
  /components
  /pages
  entry.client.tsx  # 客户端入口
  root.tsx          # 根组件
  routes.ts         # 路由配置
  i18n.ts           # 国际化配置(可选)
/public
  # 静态资源
react-router.config.ts  # React Router配置

配置Vite插件

首先,确保你的项目使用Vite构建,并安装必要的依赖。配置vite.config.ts文件,启用reactRouter插件,并设置ssr: false以禁用运行时的服务器渲染。

ts 复制代码
import {defineConfig} from 'vite'
import tailwindcss from '@tailwindcss/vite'
import {reactRouter} from "@react-router/dev/vite";

// https://vite.dev/config/
export default defineConfig({
  plugins: [reactRouter(), tailwindcss()],
})

配置react-router.config.ts文件,prerender选项用于指定需要预渲染的路径,例如:

  • 如果路径是静态的,可以直接列出:prerender: ["/", "/about"]。
  • 对于动态路由(如/product/:slug),可以使用异步函数从CMS或API获取所有可能的路径
ts 复制代码
export default {
    appDirectory: "src",
    ssr: false,
    async prerender({getStaticPaths}) {
        // getStaticPaths() 函数返回全部路由
        console.log("prerender", ...getStaticPaths())
        // 示例:从CMS获取动态路径
        const slugs = await fetchSlugsFromCMS(); // 假设的函数,根据实际情况实现
        return [
          ...getStaticPaths(), // 获取静态路径
          ...slugs.map(slug => `/product/${slug}`), // 添加动态路径
        ];
    },
} satisfies Config;

设置路由配置

src/routes.ts中定义您的路由:

ts 复制代码
import {
    type RouteConfig,
    route, index,
} from "@react-router/dev/routes";

export default [
    // * matches all URLs, the ? makes it optional so it will match / as well
    index("./pages/HomePage.tsx"),
    route("/success", "./pages/PaymentSuccess.tsx"),
    route("/cancel", "./pages/PaymentCancel.tsx"),
    route("*?", "catchall.tsx"),
] satisfies RouteConfig;

src/catchall.tsx中定义未预渲染的页面,当上面配置的路由匹配不到时在路由到下面的单页面组件继续路由:

ts 复制代码
import {Route, Routes} from "react-router";

export default function Component() {
    return <div className="min-h-screen bg-slate-900 text-white relative overflow-hidden">
        <Routes>
          <Route path="/retry-payment/:videoId" element={<RetryPayment />} />
          <Route path="/terms" element={<TermsOfService />} />
          <Route path="/privacy" element={<PrivacyPolicy />} />
        </Routes>
      </div>
}

创建根组件

src/root.tsx中创建根组件:

ts 复制代码
import {Links, Meta, Outlet, Scripts, ScrollRestoration,} from "react-router";
import Header from "./components/Header";
import Footer from "./components/Footer";

export function Layout({
                           children,
                       }: {
    children: React.ReactNode;
}) {
    return (
        <html lang="en">
        <head>
            <meta charSet="UTF-8"/>
            <link rel="icon" type="image/svg+xml" href="/ico.png"/>
            <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
            <title>VTFox - AI Audio and Video Content Recognition Tool</title>
            <Meta/>
            <Links/>
        </head>
        <body>
        <Header/>
        {children}
        <Footer/>
        <ScrollRestoration/>
        <Scripts/>
        </body>
        </html>
    );
}

export default function Root() {
    return <Outlet/>;
}

客户端入口文件

src/entry.client.tsx中设置客户端入口:

ts 复制代码
import ReactDOM from 'react-dom/client'
import './index.css'
import './i18n'
import {HydratedRouter} from "react-router/dom";
import {StrictMode} from "react";

ReactDOM.hydrateRoot(
    document,
    <StrictMode>
        <HydratedRouter />
    </StrictMode>
);

开发和构建命令

package.json中添加以下脚本:

json 复制代码
{
  "scripts": {
    "dev": "react-router dev",
    "build": "react-router build",
    "preview": "react-router preview"
  }
}

运行npm run build后,Vite会生成静态文件到build/client目录,包括每个预渲染路径的.html和.data文件。这些文件可以直接部署到静态服务器,如Netlify或GitHub Pages。

构建过程会生成build/client目录,包含:

  • url\].html:每个预渲染路径的渲染HTML文件。

例如,预渲染路径/会生成build/client/index.html和build/client/index.data。

部署与SPA回退

将build/client目录上传到静态文件服务器:

  • Netlify:上传目录,并创建_redirects文件以处理SPA回退:

    bash 复制代码
    /* /index.html 200
  • GitHub Pages:上传目录,确保404页面重定向到index.html,可能需要额外的配置。

对于ssr: false模式,如果未预渲染的路径被访问,应用会回退到SPA模式,使用客户端JavaScript处理导航。回退文件为:

  • 如果预渲染/,到build/client/index.html。
  • 如果未预渲染了/,回退到build/client/__spa-fallback.html。

这样,未预渲染的路径会回退到客户端处理。

数据加载和SEO

在路由定义中,使用loader函数预取数据,确保静态文件包含必要内容。这对SEO很重要,因为搜索引擎可以直接爬取生成的HTML。此外,记得使用管理元标签,如和。

对比与选择

React Router v7.5.0的SSG方案类似于Next.js的getStaticProps,但通过Vite插件集成,更加轻量且灵活。以下是与传统SSG框架的对比:

特性 React Router v7.5.0 SSG Next.jsgetStaticProps
配置方式 Vite插件prerender配置 文件系统路由+getStaticProps
动态路径支持 通过prerender异步函数 通过getStaticPaths
部署方式 静态文件服务器 静态导出或SSR服务器
灵活性 高(适合已有React Router项目) 高(内置支持更全面)

对于已有React Router项目的开发者,v7.5.0的SSG方案更自然;对于新项目,Next.js可能提供更开箱即用的体验。

官方文档建议,未来版本可能进一步增强框架模式(Framework Mode),支持更多SSG和SSR混合场景。

结论

React Router v7.5.0通过Vite插件的prerender配置提供了强大的SSG支持,适合需要静态部署的场景。通过设置ssr: false和配置prerender,开发者可以轻松生成静态文件,部署到静态服务器,并确保SEO和性能优化。对于动态路由,需通过异步函数明确列出所有路径,确保构建时生成完整内容。

相关推荐
江城开朗的豌豆9 分钟前
JavaScript篇:如何实现add(1)(2)(3)()=6?揭秘链式调用的终极奥义!
前端·javascript·面试
江城开朗的豌豆15 分钟前
JavaScript篇:GET、POST、PUT...傻傻分不清?一篇文章带你玩转HTTP请求!
前端·javascript·面试
恋猫de小郭2 小时前
Flutter 官方多窗口体验 ,为什么 Flutter 推进那么慢,而 CMP 却支持那么快
android·前端·flutter
云边有个稻草人3 小时前
智启未来:当知识库遇见莫奈的调色盘——API工作流重构企业服务美学
前端·数据库
仟濹7 小时前
【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】
大数据·前端·爬虫·数据挖掘·数据分析·html
小小小小宇9 小时前
前端WebWorker笔记总结
前端
小小小小宇9 小时前
前端监控用户停留时长
前端
小小小小宇9 小时前
前端性能监控笔记
前端
烛阴9 小时前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小510 小时前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步