乐闻世界logo
搜索文章和话题
React Router V6 详细教程(入门到进阶)

React Router V6 详细教程(入门到进阶)

小白的头像
小白

2024年04月26日 12:15· 阅读 561

前言

React Router 是 React 中最流行的路由库,但理解一些更复杂的功能可能有点复杂。这就是为什么在本文中我将详细介绍您需要了解的有关 React Router 的所有内容,以便您可以轻松使用最高级的功能。本文将分为 3 个部分。

  1. React 路由器基础知识
  2. 处理导航
  3. 高级路线定义

React 路由器基础知识

安装 react-router-dom

jsx
// yarn yarn add react-router-dom // npm npm install react-router-dom

配置router

首先您需要做的就是导入您需要的特定路由器 BrowserRouter 并将整个应用程序包装在该路由器中

jsx
import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import { BrowserRouter } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> );

定义Routes

React Router 的下一步是定义Routes。这通常在应用程序的顶层完成,例如在 App组件中,但也可以在您想要的任何地方完成。

jsx
import { Link, Route, Routes } from "react-router-dom"; import Home from "./pages/Home"; import BookList from "./pages/BookList"; function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> </Routes> ); }

定义路由非常简单,只需为应用程序中的每个路由定义一个 Route 组件,然后将所有这些 Route 组件放在一个 Routes 组件中。无论何时 URL 发生变化,React 路由器都会查看在 Routes 组件中定义的路由,并呈现与 URL 路径匹配的 Route element中的内容。在上面的例子中,如果我们的 URL 是/books,那么将呈现 BookList 组件。

React Router 的好处是,当您在页面之间导航时,它只会刷新组件内的内容 Routes。页面上的所有其余内容将保持不变,这有助于提高性能和用户体验。

处理导航

react router的最后一步是处理导航。通常在应用程序中,您会使用锚标记进行导航,但 React 路由器使用其自定义的 Link 组件来处理导航。这个 Link 组件只是一个锚标记的包装器,它可以帮助确保所有的路由和条件重新呈现都得到了正确的处理,这样您就可以像使用普通的锚标记一样使用它。

jsx
import { Link, Route, Routes } from "react-router-dom"; import Home from "./pages/Home"; import BookList from "./pages/BookList"; function App() { return ( <> <nav> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/books">Books</Link> </li> </ul> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> </Routes> </> ); } export default App;

在我们的示例中,我们添加了两个指向主页和书籍页面的链接。您还会注意到,我们使用 toprop 来设置 URL,而不是 href您习惯于与锚标记一起使用的 prop。这是组件和锚标记之间的唯一区别 Link,您需要记住这一点,因为不小心使用 hrefprop 而不是 toprop 是很容易犯的错误。

关于我们的新代码,需要注意的另一件事是,我们在页面顶部渲染的导航位于 Routes组件之外,这意味着当我们更改页面时,该导航部分不会重新渲染,因为只有组件中的内容 Routes会更改当网址改变时。

Untitled

高级Routes定义

动态路由

例中的最后一个路由是动态路由,其动态参数为 :id。在 React Router 中定义动态路由非常简单,只需在您希望路由的动态部分前面放置一个冒号即可。在我们的例子中,我们的动态路由将匹配任何以某个值开头 /book和结尾的 URL。例如,/books/1/books/bookName、 和 都 /books/literally-anything将匹配我们的动态路线。

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> </Routes>

Untitled

路由优先级

如果我们有 URL,/books/new该路由会匹配哪条路由?从技术上讲,我们有两条匹配的路线。和 /books/:id都会 /books/new匹配,因为动态路由只会假设这 new:idURL 的一部分,因此 React Router 需要另一种方法来确定要渲染的路由。

在旧版本的 React Router 中,首先定义的路由将是渲染的路由,因此在我们的例子中,/books/:id将渲染路由,这显然不是我们想要的。幸运的是,React Router 的第 6 版改变了这一点,所以现在 React Router 将使用一种算法来确定哪条路由最有可能是您想要的。在我们的例子中,我们显然想要渲染 /books/new路线,因此 React Router 将为我们选择该路线。该算法的实际工作方式与 CSS 特异性非常相似,因为它将尝试确定与我们的 URL 匹配的路由是最具体的(具有最少数量的动态元素),并且它将选择该路由。

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> <Route path="/books/new" element={<NewBook />} /> </Routes>

Untitled

当我们讨论路由优先级的主题时,我还想谈谈如何创建匹配任何内容的路由。

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookList />} /> <Route path="/books/:id" element={<Book />} /> <Route path="/books/new" element={<NewBook />} /> <Route path="*" element={<NotFound />} /> </Routes>

*将匹配任何内容,这使其非常适合 404 页面等内容。路由包含 *也将比其他路由更不具体,因此当另一个路由也匹配时,您永远不会意外地匹配该路由

嵌套路由

在上面的示例中,我们有三个以 开头的路由 /books,因此我们可以将这些路由嵌套在一起以简化我们的路由。页面效果是一样的

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>

包含index为true的route代表该 Route 的路径与父 Route 相同。

现在,如果这就是您对嵌套路由所能做的所有事情,那么它的用处就很小,但嵌套路由的真正威力在于它如何处理共享布局。

共享布局

假设我们想要渲染一个导航部分存在于所有涉及书籍的页面,其中包含每本书的链接以及新书籍表单。通常要执行此操作,我们需要创建一个共享组件来存储此导航,然后将其导入到每个与书籍相关的组件中。不过,这有点痛苦,因此 React Router 创建了自己的解决方案来解决这个问题。如果您将 elementprop 传递给父路由,它将为每个子页面呈现该组件,Route这意味着您可以轻松地将共享导航或其他共享组件放置在每个子页面上。

jsx
import React from "react"; import { Link, Outlet } from "react-router-dom"; export default function BookLayout() { return ( <nav> <ul> Book Layout <li> <Link to="/books/1">Book 1</Link> </li> <li> <Link to="/books/2">Book 2</Link> </li> <li> <Link to="/books/new">New Book</Link> </li> </ul> <Outlet /> </nav> ); }
jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>

我们的新代码的工作方式是,每当我们匹配 /book父级内部的路由时 Route,它就会渲染 BooksLayout包含共享导航的组件。然后,无论该组件放置在布局组件中的 Route哪个位置,匹配的子组件都将被渲染。OutletOutlet组件本质上是一个占位符组件,它将呈现我们当前页面的内容。这种结构非常有用,并且使得在路由之间共享代码变得非常容易

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books" element={<BookLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route element={<OtherLayout />}> <Route path="/contact" element={<Contact />} /> <Route path="/about" element={<About />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>

这段代码将创建两个路由/contact 和/about,它们都在 OtherLayout 组件中呈现。如果您希望这些路由共享一个布局,即使它们没有相似的路径,那么这种没有加 path 的父路由组件中包装多个 Route 组件的技术非常有用。

效果图

Untitled

多条Routes

使用 React Router 可以做的另一件非常强大的事情是 Routes同时使用多个组件。这可以作为两个单独的 Routes组件或嵌套来完成 Routes

分离 Routes

如果您想要呈现两个不同的内容部分,并且这两个部分都依赖于应用程序的 URL,那么您需要多个 Routes组件。这是很常见的,例如,如果您有一个侧边栏,您想要在某些 URL 中呈现某些内容,并且还有一个应根据 URL 显示特定内容的主页。

jsx
<aside> <Routes> <Route path="/books" element={<BookLayout />} /> </Routes> </aside> <Routes> <Route path="/" element={<Home />} /> <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> <Route path="*" element={<NotFound />} /> </Routes>

在上面的例子中,我们有两条路线。主路由定义了我们页面的所有主要组件,然后我们在旁边有一个辅助路由,当我们处于/books 时,它将为我们的 books 页面呈现侧边栏。这意味着,如果我们的 URL 是/books,那么两个 Routes 组件都将呈现内容,因为它们的 Routes 中都有一个唯一的/books 匹配项。

Untitled

使用多个 Routes 组件可以做的另一件事是硬编码位置支持。

jsx
<Routes location="/books"> <Route path="/books" element={<BookLayout />} /> </Routes>

通过像这样的硬编码location prop,我们覆盖了默认行为或者 React 路由器,所以不管我们页面的 URL 是什么,这个 Routes 组件都会匹配它的 Route,就像 URL 是/books 一样。

Untitled

嵌套 Routes

使用多个 Routes组件的另一种方法是将它们嵌套在一起。如果您有很多路由并且希望通过将相似的路由移至它们自己的文件中来清理代码,那么这种情况很常见。

jsx
<Routes> <Route path="/" element={<Home />} /> <Route path="/books/*" element={<BookRoute />} /> {/* <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> </Route> */} <Route path="*" element={<NotFound />} /> </Routes>
jsx
export default function BookRoute() { return ( <Routes> <Route element={<BookLayout />}> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> <Route path="new" element={<NewBook />} /> <Route path="*" element={<NotFound />} /> </Route> </Routes> ); }

在 React Router 中嵌套 Routes非常简单。您需要做的就是创建一个新组件来存储嵌套的 Routes该组件应该有一个 Routes组件,并且该 Routes组件内部应该是 Route与父级匹配的所有组件 Route。在我们的例子中,我们将所有 /books路由移至此 BookRoute组件中。然后在父级中,Routes您需要定义一个 Route路径,该路径等于所有嵌套 Routes共享的路径。在我们的例子中,那就是 /books。但重要的是,您需要以 Route patha结束父级 *路由,否则它将无法正确匹配子级路由。

本质上,我们编写的代码表示,每当路由以 /book/它开头时,就应该在组件内部进行搜索 BookRoutes,看看它们是否是 Route匹配的。这也是为什么我们有另一个 *路由,BookRoutes以便我们可以确保如果我们的 URL 与任何一个不匹配,BookRoutes它都会正确呈现 NotFound组件

useRoutes

关于在 React Router 中定义路由,您需要了解的最后一件事是,如果您愿意,可以使用 JavaScript 对象而不是 JSX 来定义路由。

jsx
import { Route, Routes } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" import { Book } from "./Book" export function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/books"> <Route index element={<BookList />} /> <Route path=":id" element={<Book />} /> </Route> </Routes> ) }
jsx
import { Route, Routes } from "react-router-dom" import { Home } from "./Home" import { BookList } from "./BookList" import { Book } from "./Book" export function App() { const element = useRoutes([ { path: "/", element: <Home />, }, { path: "/books", children: [ { index: true, element: <BookList /> }, { path: ":id", element: <Book /> }, ], }, ]) return element }
标签: