教程
介绍
React Router 是一个功能齐全的客户端和服务器端 React路由库,一个用于构建用户界面的 JavaScript 库。React Router 在 React 运行的任何地方运行;在web上,在带有 node.js 的服务器上,以及在 React Native 上。
如果你刚刚开始使用 React,我们建议你遵循官方文档中优秀的入门指南。那里有大量信息可以帮助你启动和运行。React Router 与 React >= 16.8 兼容。
我们将保持本教程的快速和专注点。到最后你会知道你如何日常使用 React Router 处理的 API。之后,你可以深入研究其他一些文档以获得更深入的理解。
在构建一个小记账应用时,我们将介绍:
- 配置路由
- 使用链接导航
- 创建具有激活样式的链接
- 使用嵌套路由进行布局
- 以编程方式导航
- 使用 URL 参数加载数据
- 使用 URL Query参数
- 通过组合创建自己的行为
- 服务端渲染(译者: 官方文档还没有,更新后我会同步)
安装
推荐: StackBlitz
要完成本教程,你需要一个可用的 React 应用。我们建议跳过打包程序并通过在StackBlitz的演示上在浏览器中进行编码:
当你编辑文件时,本教程将实时更新。
使用打包工具
随意使用你选择的打包器,例如Create React App或Vite。
# create react app
npx create-react-app router-tutorial
# vite
npm init vite@latest router-tutorial --template react
然后安装 React Router 依赖包:
cd router-tutorial
npm add react-router-dom@6 history@5
然后在你的 App.js 中写入"Bookkeeper(无聊)!"的文字:
export default function App() {
return (
<div>
<h1>Bookkeeper!</h1>
</div>
);
}
其实那个"!" 看起来一点都不无聊。这非常令人兴奋。我们在 React Router v6 beta 上开发了一年多,因为我们在全球大流行之后调整了我们的业务。这是我们最近做过的最令人兴奋的事情!
最后,确保index.js
或main.jsx
(取决于你使用的打包器)实际可用:
import { render } from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
render(<App />, rootElement);
最后启动你的应用:
# probably this
npm start
# or this
npm run dev
连接到URL
首先,我们希望将你的应用连接到浏览器的 URL:导入BrowserRouter
并围绕你的整个应用渲染它。
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<App />
</BrowserRouter>,
rootElement
);
你的应用中没有任何变化,但现在我们已准备好开始处理 URL。
添加一些链接
打开src/App.js
、导入Link
并添加一些全局导航。旁注:在本教程中不要太认真对待样式,我们只是为了方便而使用内联样式,你可以根据需要设置应用的样式。
import { Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Bookkeeper</h1>
<nav
style={{
borderBottom: "solid 1px",
paddingBottom: "1rem"
}}
>
<Link to="/invoices">Invoices</Link> |{" "}
<Link to="/expenses">Expenses</Link>
</nav>
</div>
);
}
继续并单击链接和后退/前进按钮(如果你使用的是 StackBlitz,则需要单击内嵌浏览器工具栏中的"在新窗口中打开"按钮)。现在React Router 正在控制 URL!
我们还没有在 URL 更改时渲染任何路由,但 Link 正在更改 URL,而不会导致整个页面重新加载。
添加一些路由
添加几个新文件:
src/routes/invoices.jsx
src/routes/expenses.jsx
(文件的位置无关紧要,但是当你决定为此应用使用自动后端 API、服务器渲染、代码拆分打包器等时,以这种方式命名你的文件可以轻松将此应用程序移植到我们的另一个项目,Remix 😉)
现在用一些代码填充它们:
export default function Expenses() {
return (
<main style={{ padding: "1rem 0" }}>
<h2>Expenses</h2>
</main>
);
}
export default function Invoices() {
return (
<main style={{ padding: "1rem 0" }}>
<h2>Invoices</h2>
</main>
);
}
最后,让我们通过在main.jsx
内创建第一个"路由配置"来学会如何使用React Router渲染我们的应用.
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="expenses" element={<Expenses />} />
<Route path="invoices" element={<Invoices />} />
</Routes>
</BrowserRouter>,
rootElement
);
注意,"/"
将渲染<App>
组件. "/invoices"
则渲染<Invoices>
。干得好!
请记住,如果你使用 StackBlitz 单击内嵌浏览器工具栏中的"在新窗口中打开"按钮,以便能够单击浏览器中的后退/前进按钮。
嵌套路由
单击链接时,你可能已经注意到布局App
消失了。重复共享布局令人头疼。我们了解到,大多数 UI 是一系列嵌套布局,几乎总是映射到 URL 的片段,因此这个想法直接融入到了 React Router 中。
让我们通过做两件事来获得一些自动的、持久的布局处理:
- 在 App 路由中嵌套路由
- 渲染一个 Outlet
首先让我们嵌套路由。眼下的费用和发票的路由在应用中是平级关系,我们希望把它们变成应用的子页面:
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="expenses" element={<Expenses />} />
<Route path="invoices" element={<Invoices />} />
</Route>
</Routes>
</BrowserRouter>,
rootElement
);
当路由有子节点时,它会做两件事:
- 它嵌套了 URL (
"/" + "expenses"
和"/" + "invoices"
) - 当子路由匹配时,它将嵌套共享布局的 UI 组件:
但是, 在(2)生效之前,我们需要在"父级"的App.jsx
路由中渲染一个Outlet
。
import { Outlet, Link } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Bookkeeper</h1>
<nav
style={{
borderBottom: "solid 1px",
paddingBottom: "1rem"
}}
>
<Link to="/invoices">Invoices</Link> |{" "}
<Link to="/expenses">Expenses</Link>
</nav>
<Outlet />
</div>
);
}
现在再次单击。父路由 ( App.js
) 仍然存在,而两个<Outlet>
子路由将在 (<Invoices>
和<Expenses>
)之间交换!
正如我们稍后将看到的,这适用于路由层次结构的任何级别,并且非常强大。
列出清单
通常你会从某个地方的服务器获取数据,但在本教程中,让我们硬编码一些假数据,这样我们就可以专注于路由。
创建一个文件src/data.js
并将其复制/粘贴到那里:
let invoices = [
{
name: "Santa Monica",
number: 1995,
amount: "$10,800",
due: "12/05/1995"
},
{
name: "Stankonia",
number: 2000,
amount: "$8,000",
due: "10/31/2000"
},
{
name: "Ocean Avenue",
number: 2003,
amount: "$9,500",
due: "07/22/2003"
},
{
name: "Tubthumper",
number: 1997,
amount: "$14,000",
due: "09/01/1997"
},
{
name: "Wide Open Spaces",
number: 1998,
amount: "$4,600",
due: "01/27/2998"
}
];
export function getInvoices() {
return invoices;
}
现在我们可以在清单(发票)路由中使用它。让我们同时添加一些样式来获得侧边栏导航布局。随意复制/粘贴所有这些,但要特别注意<Link>
组件的to