React 超级教程,第 4 章:路由和​​页面导航

总目录

您在第 2 章中安装的React Router包为 SPA 实现了一个完整的页面导航系统。在本章中,您将学习如何创建客户端路由并在它们之间导航。

创建页面组件

在严格的浏览器意义上,页面的概念并不真正适用于 SPA,因为 SPA 只有一个页面。在 SPA 中,页面只是顶级应用程序状态,用于指示应用程序在浏览器中的呈现方式。与传统页面一样,SPA 中的每个页面都可以与/login/user/susan等路径相关联,但这些路径由客户端应用程序管理,永远不会到达服务器。

React-Router 包会跟踪这些页面状态,并使用适当的 URL 路径自动更新浏览器的地址栏。它还控制浏览器的后退和前进按钮,并使它们按用户期望的方式工作。

为了帮助建立一个健全的应用程序结构,映射到应用程序逻辑页面的顶级组件将被写入一个单独的目录,称为pages。现在创建这个目录:

mkdir src/pages

此应用程序的默认页面将是为用户显示帖子提要的页面。让我们将Body当前位于 中的组件移动到存储在src/pages/FeedPage.js App中的新组件。FeedPage

src/pages/FeedPage.js:Feed 页面

import Body from '../components/Body';
import Posts from '../components/Posts';

export default function FeedPage() {
  return (
    <Body sidebar>
      <Posts />
    </Body>
  );
}

请注意组件的导入路径现在如何使用../components/. 这是因为路径是相对于导入源文件的位置的,它位于src/pages中。

此应用程序中第二个最重要的页面将是“探索”页面,它将显示系统中所有用户的博客文章,旨在作为用户发现其他用户关注的地方。ExplorePage在组件中为此页面添加占位符:

src/pages/ExplorePage.js:探索页面的占位符

import Body from '../components/Body';

export default function ExplorePage() {
  return (
    <Body sidebar>
      <h1>Explore</h1>
      <p>TODO</p>
    </Body>
  );
}

即使应用程序还没有准备好登录页面,也可以为LoginPage组件创建一个占位符,以便您稍后可以测试三个不同页面之间的导航。

src/pages/LoginPage.js:登录页面的占位符

import Body from '../components/Body';

export default function LoginPage() {
  return (
    <Body>
      <h1>Login form</h1>
      <p>TODO</p>
    </Body>
  );
}

在 React-Router 包的帮助下,App组件现在可以实现这三个页面的路由。下面是新版本的App,使用了几个新的路由组件。

src/App.js : 页面路由

import Container from 'react-bootstrap/Container';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Header from './components/Header';
import FeedPage from './pages/FeedPage';
import ExplorePage from './pages/ExplorePage';
import LoginPage from './pages/LoginPage';

export default function App() {
  return (
    <Container fluid className="App">
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/" element={<FeedPage />} />
          <Route path="/explore" element={<ExplorePage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route path="*" element={<Navigate to="/" />} />
        </Routes>
      </BrowserRouter>
    </Container>
  );
}

React-Router 库被称为react-router-dom. 该库提供了四个在上面使用的组件。

BrowserRouter组件为应用程序添加路由支持。该组件必须在组件层次结构中添加到非常高的位置,因为它必须是应用程序中所有路由逻辑的父级。在渲染方面,这个组件透明地渲染它的子组件,它本身不渲染任何东西,所以它可以方便地添加为靠近 JSX 树顶部的父组件。

Routes是一个组件,需要根据当前页面插入到组件树中内容需要改变的地方。打个比方,您可以Routes将路由视为等效switch于 JavaScript 中的语句,或者是一个长链或多个if-then-else语句。

Route用于定义Routes组件内部的路由。该path属性定义路由的 URL 的路径部分,并且该element属性指定与路由关联的内容。按照 switch 语句的类比,这个组件相当于case语句。

Navigate是一个特殊的组件,它允许从一个路由重定向到另一个路由。Route上面清单中的第四个组件的路径设置为*,它用作任何与上面声明的路由不匹配的 URL 的包罗万象的路由。element此路由中的属性Navigate用于将所有这些未知 URL 重定向到根 URL。

使用此版本的应用程序,浏览器的地址栏、后退和前进按钮已连接到应用程序,并且路由功能正常。如果您在浏览器的地址栏中键入http://localhost:3000/login ,应用程序将加载并自动呈现之前添加的登录页面占位符。如果您在 URL 的路径部分使用/explore ,也会发生同样的情况。

侧边栏中的两个链接是标准浏览器链接,尚未连接到 React-Router,因此它们会触发整个页面重新加载。忽略这导致的低效率(很快就会得到修复),它们应该允许您在提要和浏览页面之间切换。

在三个页面之间移动几次后,尝试浏览器中的后退和前进按钮,看看 React-Router 如何使 SPA 像普通的多页网站一样运行。

实施链接

如上所述,侧边栏中的两个链接在允许您在页面之间导航的意义上是有效的,但是它们效率低下,因为它们是标准的浏览器链接,每次单击它们时都会重新加载整个 React 应用程序。对于单页应用程序,页面之间的路由应该在 JavaScript 内部处理,而不需要到达浏览器自己的页面导航系统。

React-Router 包提供了LinkNavLink组件来帮助生成对 SPA 友好的链接。它们之间的区别在于,Link它只是一个常规链接,而NavLink扩展了链接的行为,当其 URL 与当前页面匹配时,它能够变为“活动”,从而允许应用程序更改样式。在导航栏这样的Sidebar组件中,NavLink使用起来最有意义,因为可以突出显示当前活动的页面。

侧边栏中的现有链接使用来自 React-Bootstrap的Nav.Link组件。这个组件有一个非常相似的名字,除了和之间的点NavLink但是这两个组件是完全不同的。在当前版本的侧边栏中,使用以下语法创建链接:

 <Nav.Link href="/">Feed</Nav.Link>

如何将其与NavLinkReact-Router 的组件结合以生成 SPA 链接?React-Bootstrap 认识到很多时候它的组件需要与来自其他库的组件集成,因此Nav.Link(以及许多其他库)具有as指定不同基础组件的属性。这使得使用 React-Bootstrap 组件作为来自另一个库(例如 React-Router)的组件的包装器成为可能。

以下是如何调整上述链接以与 React-Router 一起使用NavLink,同时仍与 Bootstrap 兼容:

<Nav.Link as={NavLink} to="/">Feed</Nav.Link>

注意 的href属性是如何Nav.Link重命名为 的to,因为现在这个组件将呈现为 React-Router 的NavLink,它使用to而不是href. 使用此解决方案,与导航链接关联的 Bootstrap CSS 类被保留,但它们被应用于 React-RouterNavLink组件。

这是 React-Router 的版本Sidebar

src/components/Sidebar.js : 启用 React-Router 的侧边栏

import Navbar from "react-bootstrap/Navbar";
import Nav from "react-bootstrap/Nav";
import { NavLink } from 'react-router-dom';

export default function Sidebar() {
  return (
    <Navbar sticky="top" className="flex-column Sidebar">
      <Nav.Item>
        <Nav.Link as={NavLink} to="/" end>Feed</Nav.Link>
      </Nav.Item>
      <Nav.Item>
        <Nav.Link as={NavLink} to="/explore">Explore</Nav.Link>
      </Nav.Item>
    </Navbar>
  );
}

NavLink当 React-Router 维护的当前页面 URL 与它自己的链接地址匹配时,该组件会识别,在这种情况下,它会认为该链接是“活动的”。请注意end添加到提要链接的属性。这个属性告诉 React-Router 只有当 URL 完全匹配时才应该认为链接是活动的。如果没有end,则当 URL 以to属性中给定的路径开头时,该链接将被视为活动链接,这意味着对于此根 URL,该链接将被标记为每个页面的活动链接。

对于活动链接,NavLinkactive类名添加到其<a>元素中。可以在src/index.css中使用此类名称来为活动链接创建新样式。在index.css的底部添加以下类定义:

src/index.css : 导航链接的活动页面样式

... // <-- no changes to existing styles

.Sidebar .nav-item .active {
  background-color: #def;
}

使用这个新的 CSS 定义,您可以在提要和浏览页面之间导航,两个链接中的任何一个处于活动状态都会以浅蓝色背景呈现,如图 4.1所示。

具有动态参数的页面

对于大多数 Web 应用程序,某些页面需要在路径部分具有占位符的路由 URL。考虑为每个用户创建一个个人资料页面。为该页面定义路由的最方便的方法是在路径本身中包含用户 ID 或名称。在微博中,给定用户的个人资料页面将是/user/{username}

要定义具有动态部分的路由,组件的path属性Route使用带有冒号前缀的特殊语法:

<Route path="/user/:username" element={<UserPage />} />

:表示路径的该部分作为与任何值匹配的占位符。属性引用的组件element或其任何子组件可以使用useParams()函数将当前 URL 的动态参数作为对象访问。

这是您第一次遇到以单词开头的函数use。在 React 中,“使用”函数称为hooks。Hook 函数在 React 中很特殊,因为它们提供了对应用程序状态的访问。React 包含许多钩子,您将在后面的章节中遇到其中的大部分。许多 React 库也提供了自己的钩子,例如useParams()来自 React-Router 的钩子。应用程序也可以创建自定义挂钩,稍后您还将了解这一点。

让我们向应用程序添加一个简单版本的用户配置文件页面,现在只显示用户名。这将是UserPage组件,存储在src/pages/UserPage.js中。

src/pages/UserPage.js:一个简单的用户个人资料页面

import { useParams } from 'react-router-dom';
import Body from '../components/Body';

export default function UserPage() {
  const { username } = useParams();

  return (
    <Body sidebar>
      <h1>{username}</h1>
      <p>TODO</p>
    </Body>
  );
}

下面的清单显示了App带有用户配置文件页面的更新组件。

src/App.js : 带有动态参数的用户个人资料页面

import Container from 'react-bootstrap/Container';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import Header from './components/Header';
import FeedPage from './pages/FeedPage';
import ExplorePage from './pages/ExplorePage';
import UserPage from './pages/UserPage';
import LoginPage from './pages/LoginPage';

export default function App() {
  return (
    <Container fluid className="App">
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/" element={<FeedPage />} />
          <Route path="/explore" element={<ExplorePage />} />
          <Route path="/user/:username" element={<UserPage />} />
          <Route path="/login" element={<LoginPage />} />
          <Route path="*" element={<Navigate to="/" />} />
        </Routes>
      </BrowserRouter>
    </Container>
  );
}

与登录页面一样,新的用户个人资料页面目前没有从应用程序的任何其他部分链接,因此查看它的唯一方法是在浏览器的地址栏中键入匹配的 URL。图 4.2显示了对应于http://localhost:3000/user/susan URL 的用户页面。

章节总结

  • 使用 React-Router 在您的 React 应用程序中创建客户端路由。
  • 遵循良好组织代码的想法,为页面级组件创建一个子目录。
  • 将应用程序的每个逻辑页面创建为可以定义为路由的单独页面级组件。