发布于 2026-01-06 7 阅读
0

如何在 React Router 6 中使用嵌套路由

如何在 React Router 6 中使用嵌套路由

React Router 6 版本简化了路由嵌套。路由嵌套允许多个组件在同一页面上渲染,并保持路由一致。这对于需要用户“深入”浏览内容而不迷路的应用体验非常有用,例如论坛或博客。

图片描述

安装 React Router

首先,将 React Router 6 安装到你的 React 应用中。在应用目录中打开终端并输入:

npm install react-router-dom@6

安装完成后,进入应用的顶层组件。我个人喜欢保持index.jsx简洁标准,所以把路由放在了App.jsx上一级组件中。

首先App.jsx导入所需的组件:

import { BrowserRouter as Router, Routes, Route } from "react-router-dom"

BrowserRouter as Router由于BrowserRouter输入内容太长,导入是标准做法!

基本路由

从这里开始,您可以配置路由。一个空的嵌套路由框架如下所示,最外层的组件<Router>包裹着一个<Routes>(复数!)组件,该组件又包裹着多个<Route>(单数!)组件:




import { BrowserRouter as Router, Routes, Route } from "react-router-dom"

export default function App() {
    return (
        <Router>
            <Routes>
                <Route>
                    <Route />
                    <Route />
                </Route>
            </Routes>
        </Router>
    )
}


Enter fullscreen mode Exit fullscreen mode

<Route>组件接收一个 `a`pathelement`b` 属性。`a`path值保存路由路径。`b`element值保存指向组件或页面的指针。

<Route path='/' element={<Home />} />是指向Home基本路由中的组件的路由https://yourblog.com/

让我们尝试创建一个博客应用,以嵌套方式显示文章。我们希望在一个组件中显示文章预览<Posts/>列表。最终,我们希望能够点击文章预览来显示完整文章内容的新路由。我们还希望点击一个按钮,<Posts/>在与预览相同的位置弹出新的文章表单。<Post/>让我们首先向组件添加一些路由App.jsx




import { BrowserRouter as Router, Routes, Route } from "react-router-dom" 

export default function App() {
    return (
        <Router>
            <Routes>
                <Route path='/' element={<Home />} />
                <Route path='about' element={<About />} />
                <Route path='posts' element={<Posts />} />
            </Routes>
        </Router>
    )
}


Enter fullscreen mode Exit fullscreen mode

图片描述

要使此功能生效,您需要在页面顶部导入“首页”、“关于”和“文章”组件。

这些独立的路由现在分别指向不同的组件。这些路由尚未嵌套。如果我们访问https://yourblog.com/about网页,只会渲染组件内部的内容<About />https://yourblog.com/posts<Posts />

上述应用程序提供以下路由:

  • "/"
  • "/about"
  • "/posts"

请注意,在您的pathprops 中,我们不写 `/path/to/path` /about,而是写 `/path/to/path` 。在 React Router 6 中,路径名中的about正斜杠是隐式的。/

嵌套路由

但我们想要嵌套路由!这很简单:



export default function App() {
    return (
        <Router>
            <Routes>
                <Route path='/' element={<Home />} />
                <Route path='about' element={<About />} />
                <Route path='posts' element={<Posts />}>
                    <Route path='new' element={<NewPost />} /> {/*A nested route!*/}
                    <Route path=':postId' element={<Post />} /> {/*A nested route!*/}
                </Route>
            </Routes>
        </Router>
    )
}


Enter fullscreen mode Exit fullscreen mode

在组件之间Route可以嵌入另一个路由:<Route>{/* Children routes go here*/}</Route>

现在我们的路由结构如下所示:

  • "/"
  • "/about"
  • "/posts"
  • "/posts/new"
  • "/posts/123"

现在我们可以学习这些嵌套路由如何相互传递信息,并确保它们以正确的“嵌套”方式显示。

使用 <Outlet/> 嵌套路由

图片描述

/posts/new指向一个用于创建新帖子的页面。该页面可能包含一个用于提交新帖子的表单。

当我们访问该页面时,https://yourblog.com/posts/new会看到<Posts />组件 1 和<NewPost />组件 2。这就是路由嵌套的魔力!我们用同一个描述性路径在同一个页面上渲染两个不同的组件。太棒了!

此时,如果您一直跟着操作,可能看不到<NewPost />组件出现。这是因为我们需要告诉父组件<Posts />在哪里渲染它的子组件。React Router 提供了一个名为 `router` 的组件Outlet,用于渲染路由的子组件。

一个<Posts />组件可能如下所示:



import { Outlet } from "react-router-dom"

export default function Posts() {
    return (
        <div>
            <h1>List of posts go here!</h1>
            <Outlet />
        </div>
    )
}


Enter fullscreen mode Exit fullscreen mode

<Outlet />props.children其行为与标准 React类似。<Outlet />是嵌套子路由的渲染位置的占位符。

您可能想知道如何将 props 从父路由传递给子路由。React Router 6 有一个Outlet名为`props` 的原生 prop context,它实际上是一个React 上下文提供程序context,接受一个状态数组

为了使子路由能够接受上下文,子组件必须使用 React Router 提供的 hook useOutletContext。整个 getup 过程可能如下所示:



// Posts.jsx, the parent
import { Outlet } from "react-router-dom"

export default function Posts() {
  const [currentUser, setCurrentUser] = React.useState([/*array of post content*/])

    return (
        <div>
            <h1>List of posts go here!</h1>
            <Outlet context={[currentUser]}/>
        </div>
    )
}

//NewPost.jsx, the child
import { useOutletContext } from "react-router-dom"

export default function NewPost() {
  const [currentUser] = useOutletContext()

    return (
        <div>
            <h1>Welcome {currentUser}, write a new post!</h1>
      <form/>
        </div>
    )
}


Enter fullscreen mode Exit fullscreen mode

为了出口:

  • 父级必须导入Outlet
  • 父元素必须嵌入<Outlet/>context=[state]渲染中
  • 该儿童必须导入useOutletContext
  • 子对象必须使用 `.` 来解构数组useOutletContext()。顺序很重要,这与 props 和对象解构不同。

现在,我们来看下一条嵌套路由:postId

嵌套路由和 useParams

图片描述

/posts/123将显示特定帖子。此路由的 prop 是一个参数,由路径中的path前缀进行缩进:":"

<Route path=':postId' element={<Post />} />

<Post />组件中,我们可以通过导入 useParams 来获取参数123

import { useParams } from "react-router-dom"

然后在你的函数组件的主体中,调用 useParams。

let params = useParams()

现在,在组件的范围内123,您可以通过调用来访问路由中传递的任何帖子 ID params.postId



import { useParams } from "react-router-dom"

function Post() {
    let params = useParams()

    return <h1>{params.postId}</h1>
}


Enter fullscreen mode Exit fullscreen mode

带索引的占位符组件

路由嵌套可以超过一层。我们再嵌套一层路由<Post />来显示评论。



export default function App() {
    return (
        <Router>
            <Routes>
                <Route path='/' element={<Home />} />
                <Route path='about' element={<About />} />
                <Route path='posts' element={<Posts />}>
                    <Route path='new' element={<NewPost />} />
                    <Route path=':postId' element={<Post />}>
                        <Route index element={<PostIndex />} />
                        <Route path='comments' element={<Comments />} />
                    </Route>
                </Route>
            </Routes>
        </Router>
    )
}


Enter fullscreen mode Exit fullscreen mode

现在我们的路由结构如下所示:

  • "/"
  • "/about"
  • "/posts"
  • "/posts/new"
  • "/posts/123"
  • "/posts/123/comments"

正如预期,<Comments />现在它会渲染为 的子路由<Post />。当然,现在<Post />我们需要在 中添加一个<Outlet />来渲染子路由。它看起来会像这样:

图片描述

等等,我们添加了两条路由,为什么没有路径<PostIndex />

<Route index element={<CommentEmpty />} />没有 path 属性,而是被赋予一个index值。

index声明此路由为父路由Outlet在没有其他子路由需要渲染时要渲染的默认子路由。index是空路径的默认占位符内容。

在这种情况下,如果没有/comments路径,渲染结果将如下所示:

图片描述

您可以在这里添加一个干净的水印,以提升用户体验,或者添加一些关于这篇文章的有趣统计数据,这完全取决于您!但您可以选择在这里添加一些内容,而不是一片看起来很糟糕的空白。

在嵌套路由之间导航

最后,React Router 提供了一种方便的方法来上下导航嵌套路由。

该库提供了一个名为 hook 的功能useNavigate(),允许您开发程序化的路径更改。

使用方法:将其导入到用户希望能够导航离开的组件顶部。

import { useNavigate } from "react-router-dom"

然后将其实例化到组件主体中。

let navigate = useNavigate()

现在你可以用它navigate({/*options*/})来渲染不同的路由。



import { useNavigate } from "react-router-dom"

function Post() {
    let navigate = useNavigate()

    return (
        <div>
            <button onClick={() => navigate("./")}>Go Back One</button>
            <button onClick={() => navigate("../")}>Go Back Two</button>
        </div>
    )
}


Enter fullscreen mode Exit fullscreen mode

useNavigate() 的一些实用选项包括:

  • 使用 `useNavigate("./")` 进入嵌套路径
  • 使用 useNavigate(-1) 可以返回上一页,就像用户点击了浏览器中的后退按钮一样。
  • 使用 `useNavigate("/pathname")` 跳转到指定路径

结尾

希望这能帮助你开发出一些很棒的嵌套路由应用程序!

资源

一篇更深入的实用博客文章:
https://ui.dev/react-router-nested-routes

React Router 6 文档:
https://reactrouter.com/docs/en/v6/api

文章来源:https://dev.to/tywenk/how-to-use-nested-routes-in-react-router-6-4jhd