使用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和性能优化。对于动态路由,需通过异步函数明确列出所有路径,确保构建时生成完整内容。

相关推荐
喝拿铁写前端几秒前
你以为你在封装组件,其实你在引入混乱
前端·架构
Json____12 分钟前
智慧酒店企业站官网-前端静态网站模板【前端练习项目】
前端·网站模板·静态网站·企业站·智慧酒店网站
不爱说话郭德纲12 分钟前
没有CICD,怎么自动化部署?
前端·javascript·vue.js
Aotman_12 分钟前
Vue表单组件el-form校验规则rules,条件判断rules表单验证显示必填或非必填
javascript·vue.js·elementui
哔哩哔哩技术14 分钟前
漫画产业加密技术探索与实践:抵御盗版的创新之路
前端
YYDS31415 分钟前
坦克大战HTML网页游戏 (永久免费)
javascript·游戏·html
开心小老虎17 分钟前
ThreeJs实现裸眼3D地球仪
前端·3d·threejs
大强的博客31 分钟前
《Vue Router实战教程》21.扩展 RouterLink
前端·javascript·vue.js
@是你太难忘35 分钟前
6.4案例:使用渲染函数渲染列表
前端·javascript·vue.js