React Router 已经升级到了版本6, 与版本5有了挺大区别,让我们跟着官方教程来学习一下 React Router 6 的使用吧。
安装
使用 vite 创建一个项目
yarn create vite router-tutorial --tempalte react
安装 React Router 6
yarn add react-router-dom@6
编辑 App.jsx
export default function App() {return (<div><h1>Bookkeeper!</h1></div>);}
编辑 main.jsx
import { render } from "react-dom";import App from "./App";const rootElement = document.getElementById("root");render(<App />, rootElement);
连接URL
用 BrowserRouter 包裹住 App 组件, 应用就能与浏览器的 URL 连接起来了
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);
添加Links
Link 的作用是在不导致页面重新加载的情况下切换浏览器的URL
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>);}
添加 Routes
创建2个新文件
- src/routes/invoices.jsx
- src/routes/expenses.jsx
内容如下
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 中添加 Route, 告诉浏览器如何根据不同的 URL 来渲染我们的 app
import { render } from "react-dom";import App from "./App";import {BrowserRouter,Routes,Route} from "react-router-dom";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);
这样设置路由会有一个问题: 当点击一个 Link 时,整个页面都会变成对应组件的内容,从 App 组件跳转到 Invoices 组件时,App 组件的导航栏就看不到了
嵌套路由
为了解决组件间共享布局的问题,我们可以使用嵌套路由,让子组件在父组件的某个区域内渲染
将子组件的 Route 嵌套到父组件的 Route 里面
<Routes><Route path="/" element={<App />}><Route path="expenses" element={<Expenses />} /><Route path="invoices" element={<Invoices />} /></Route></Routes>
此时再点击 Link 只有 URL 改变,并不会渲染对应的组件
在父组件中添加 Outlet 就能渲染子组件了
import { Link, Outlet } 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>);}
添加 Invoices 列表
创建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/1998',},]export function getInvoices() {return invoices}
import { Link } from "react-router-dom"import { getInvoices } from "../data"export default function Invoices() {let invoices = getInvoices()return (<div style={{display: "flex"}}><nav style={{borderRight: "solid 1px",padding: "1rem"}}>{invoices.map((invoice) => (<Link style={{display: "block", margin: "1rem 0"}}to={`/invoices/${invoice.number}`}key={invoice.number}>{invoice.name}</Link>))}</nav></div>)}
点击 Link 跳转到对应的 URL, 此时页面为空白,因为我们没有设置与该 URL 匹配的路由
添加 404 not found
当其他 route 都不匹配时, 就会与 * 匹配
// ...const rootElement = document.getElementById("root");render(<BrowserRouter><Routes><Route path="/" element={<App />}><Route path="expenses" element={<Expenses />} /><Route path="invoices" element={<Invoices />} /><Routepath="*"element={<main style={{padding: "1rem"}}><p>There's nothing here!</p></main>} /></Route></Routes></BrowserRouter>,rootElement);
读取URL参数(params)
创建 Inovice 组件
export default function Invoice() {return <h2>Invoice #???</h2>}
在 Invoices 的 Route 中添加子 Route
<Route path="invoices" element={<Invoices />} ><Route path=":invoiceId" element={<Invoice />} /></Route>
在 Invoices 组件中添加 Outlet 组件, 当点击子路由时就会显示子组件
使用 useParams 获取 Route 中的参数 :invoiceId
import { useParams } from "react-router-dom"export default function Invoice() {let params = useParams()return <h2>Invoice: {params.invoiceId}</h2>}
index Route

当没有点击 Invoices 组件内的 list 时, Invoices 的 Outlet 区域显示为空白,我们可以添加一个 index route 来指定 Outlet 区域默认显示的内容
<Route path="invoices" element={<Invoices />} ><Routeindexelement={<main style={{padding: "1rem"}}><p>Select an invoice</p></main>}></Route><Route path=":invoiceId" element={<Invoice />} /></Route>

index Route 没有 path 属性, 它的 path 就是父Route的path, 当父Route被选择而子Route未被选择,则父Route的 Outlet 里默认显示 index Route 的内容
Active Links
使用 Link 标签无法知道 Link 是否被选中,将 Link 改为使用 NavLink
<NavLinkstyle={( {isActive} ) => ({display: "block",margin: "1rem 0",color: isActive ? "red" : ""})}to={`/invoices/${invoice.number}`}key={invoice.number}>{invoice.name}</NavLink>
style 在这里变成了一个函数,参数是一个有 isActive 属性的对象
被选中的 NavLink 会被添加上 active 类名
NavLink 的 className 也可以使用函数
// normal string<NavLink className="red" />// function<NavLink className={({ isActive }) => isActive ? "red" : "blue"} />
search params
URL 除了传递参数,还可以传递查询字符串,例如:/shoes?brand=nike&sort=asc&sortby=price
使用useSearchParams可以读取和修改查询字符串
- useSearchParams的用法类似于useState
- searchParams.get() 方法获得查询字符串指定属性的值
- setSearchParams() 修改查询字符串
```jsx
import { Link, NavLink, Outlet, useSearchParams } from “react-router-dom”
import { getInvoices } from “../data”
export default function Invoices() { let invoices = getInvoices() let [searchParams, setSearchParams] = useSearchParams()
return (

