如何在 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>
)
}
该<Route>组件接收一个 `a`path和element`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>
)
}
要使此功能生效,您需要在页面顶部导入“首页”、“关于”和“文章”组件。
这些独立的路由现在分别指向不同的组件。这些路由尚未嵌套。如果我们访问该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>
)
}
在组件之间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>
)
}
<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>
)
}
为了出口:
- 父级必须导入
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>
}
带索引的占位符组件
路由嵌套可以超过一层。我们再嵌套一层路由<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>
)
}
现在我们的路由结构如下所示:
"/""/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>
)
}
useNavigate() 的一些实用选项包括:
- 使用 `useNavigate("./")` 进入嵌套路径
- 使用 useNavigate(-1) 可以返回上一页,就像用户点击了浏览器中的后退按钮一样。
- 使用 `useNavigate("/pathname")` 跳转到指定路径
结尾
希望这能帮助你开发出一些很棒的嵌套路由应用程序!
资源
一篇更深入的实用博客文章:
(https://ui.dev/react-router-nested-routes)
React Router 6 文档:
https://reactrouter.com/docs/en/v6/api





