React Router v6 官方文档翻译 (一) ---- Installation && Quick Start
专栏说明:对于react-router v6版本,有同事反应缺少相对完整的中文文档,查看不方便。为了便于使用,作者对官网文档进行针对性翻译,便于v6版本更广泛的被使用。
安装
本文档介绍使用基于React框架的不同构建工具来配置React Router的一般方案。
基础安装
大多数React工程项目都使用包管理器npm 或者 Yarn进行依赖管理。使用React Router前的第一件事情,就是基于你的包管理工具进行React Router的安装。
- npm
$ npm install react-router-dom@6
- yarn
$ yarn add react-router-dom@6
- pnpm
$ pnpm add react-router-dom@6
基于Create-react-app项目的安装
当你在cra项目中安装好依赖后,打开文件src/index.js
, 从 react-router-dom
引入 BrowserRouter
, 并使用标签<BrowserRouter>
包裹文件:
import * as React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
接下来你就可以在app中任意地方使用路由了:
import * as React from "react";
import { Routes, Route, Link } from "react-router-dom";
import "./App.css";
function App() {
return (
<div className="App">
<h1>Welcome to React Router!</h1>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
上述文件中,Home和About是自定义的路由组件,通过import引入。
基于Parcel项目的安装
在package.json中添加脚本:
"scripts": {
"start": "parcel index.html"
}
安装完依赖后,在根目录下新建文件.babelrc
:
{
"presets": ["@babel/preset-react"]
}
然后在根目录的index.js
文件中即可使用:
import * as React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App.js";
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
写入文件index.html
:
<body>
<noscript
>You need to enable JavaScript to run this
app.
</noscript>
<div id="root"></div>
<script src="./index.js"></script>
</body>
接下来就可以在App组价中使用router了,使用方式同cra项目。
基于Webpack项目的安装
webpack是一种更细粒度的构建工具,您可以进行更加细微的配置。安装完必要的依赖后,便可以引入路由了:
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
function App() {
return (
<BrowserRouter>
<div>
<h1>Hello, React Router!</h1>
<Routes>
<Route path="/" element={<Home />} />
</Routes>
</div>
</BrowserRouter>
);
}
使用script标签全局安装
React-router v6与React16.8+完全兼容。您完全可以在一个HTML中通过<script>
引入对应的js文件进行全局使用:(我这里删掉了原文的注释)
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@>=16.8/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@>=16.8/umd/react-dom.development.js" crossorigin></script>
<!-- Load history -->
<script src="https://unpkg.com/history@5/umd/history.development.js" crossorigin></script>
<!-- Load React Router and React Router DOM -->
<script src="https://unpkg.com/react-router@6/umd/react-router.development.js" crossorigin></script>
<script src="https://unpkg.com/react-router-dom@6/umd/react-router-dom.development.js" crossorigin></script>
<!-- A simple example app -->
<script>
var e = React.createElement;
var Router = ReactRouterDOM.BrowserRouter;
var Routes = ReactRouterDOM.Routes;
var Route = ReactRouterDOM.Route;
ReactDOM.render(
(
e(Router, null, (
e(Routes, null, (
e(Route, {
element: e('div', null, 'Hello, React Router!')
})
))
))
),
document.getElementById('root')
);
</script>
</body>
虽然这种方法来得比较快,但是也引入了一些不必要的代码引用。React Router包含了很多小工具和组件,按需引入才是王道。所以还是推荐前三种安装方式。
快速开始(有一些与安装重复)
引入
安装依赖,前边已经提到:
npm install react-router-dom@6
配置路由
import ReactDOM from "react-dom/client";
import {
BrowserRouter,
Routes,
Route,
} from "react-router-dom";
// import your route components too
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
);
在v6之前的版本,有多路由要匹配时,需要合理安排前后放置顺序。在v6中,匹配更智能,不要在担心顺序问题了,举个例子:
<Route path="teams/:teamId" element={<Team />} />
<Route path="teams/new" element={<NewTeamForm />} />
路由teams/new
似乎对于上边两个路径都可以匹配,但是第二个更精确,所以v6 router会选择第二个NewTeamForm。
路由跳转
可使用Link
标签来点击改变URL,或者使用useNavigate
钩子通过代码跳转。
- Link
import { Link } from "react-router-dom";
function Home() {
return (
<div>
<h1>Home</h1>
<nav>
<Link to="/">Home</Link> |{" "}
<Link to="about">About</Link>
</nav>
</div>
);
}
- navigate
import { useNavigate } from "react-router-dom";
function Invoices() {
let navigate = useNavigate();
return (
<div>
<NewInvoiceForm
onSubmit={async (event) => {
let newInvoice = await createInvoice(
event.target
);
navigate(`/invoices/${newInvoice.id}`);
}}
/>
</div>
);
}
获取路由参数
可以使用 :style
形式传参, 使用 useParams()
获取参数:
import { Routes, Route, useParams } from "react-router-dom";
function App() {
return (
<Routes>
<Route
path="invoices/:invoiceId"
element={<Invoice />}
/>
</Routes>
);
}
function Invoice() {
let params = useParams();
return <h1>Invoice {params.invoiceId}</h1>;
}
举一个通过路由参数获取异步数据的例子:
function Invoice() {
let { invoiceId } = useParams();
// useFakeFetch可以理解为一个异步请求
let invoice = useFakeFetch(`/api/invoices/${invoiceId}`);
return invoice ? (
<div>
<h1>{invoice.customerName}</h1>
</div>
) : (
<Loading />
);
}
2023.3.9 补充:这里顺便补充一下获取问号传参的方式,方便查询
问号传参获取
v6 中
// 引入
import { useSearchParams } from 'react-router-dom';
...
// 获取
const [searchParams] = useSearchParams();
// 使用
const currentType = searchParams.get('type');
v5中
v5 版本没有这个hook,需要自己定义一下:
// useSearchParams.js
import { useLocation } from "react-router-dom";
import { useMemo } from "react";
/**
* 获取当前页面的查询参数
*/
export default function useSearchParams() {
const { search } = useLocation();
return useMemo(() => {
const urlSearchParams = new URLSearchParams(search);
let params = {};
urlSearchParams.forEach((value, key) => {
params[key] = value;
});
return params;
}, [search]);
}
使用:
const searchParams = useSearchParams();
console.log(searchParams.type)
路由嵌套
路由嵌套是一个很强大的功能,可以减少冗杂的布局代码,降低布局的难度。如下使用:
function App() {
return (
<Routes>
<Route path="invoices" element={<Invoices />}>
<Route path=":invoiceId" element={<Invoice />} />
<Route path="sent" element={<SentInvoices />} />
</Route>
</Routes>
);
}
分别通过如下三种路径匹配:
"/invoices"
"/invoices/123"
"/invoices/sent"
Outlet
可以使用一个路由占位符,针对多匹配的路由位置进行复用,类似于vue router的路由插槽和Angular的router-outlet
:
import {
Routes,
Route,
Link,
Outlet,
} from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route path="invoices" element={<Invoices />} />
<Route path="dashboard" element={<Dashboard />} />
</Route>
</Routes>
);
}
function Layout() {
return (
<div>
<h1>Welcome to the app!</h1>
<nav>
<Link to="invoices">Invoices</Link> | {" "}
<Link to="dashboard">Dashboard</Link>
</nav>
<div className="content">
<Outlet />
</div>
</div>
);
}
function Invoices() {
return <h1>Invoices</h1>;
}
function Dashboard() {
return <h1>Dashboard</h1>;
}
这样会把URL片段构建为组件树,路由下的子路由会动态的去替换掉父路由的<Outlet />
,以此达到嵌套的目的。
默认路由
当一个父路由有多个子路由,且当前URL停留在父路由时(上个例子中,路由为'/'时,outlet里就无法识别渲染),界面该如何渲染呢?你需要给父路由设置一个默认渲染的子路由。
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Activity />} />
<Route path="invoices" element={<Invoices />} />
<Route path="activity" element={<Activity />} />
</Route>
</Routes>
);
}
加上index
属性后就会默认渲染该路由下的组件。而且默认索引路由可以放在路由嵌套的任何一级中使用。
嵌套路由中使用Link
...
// <Dashboard />
<nav>
<Link to="invoices">Invoices</Link>{" "}
<Link to="team">Team</Link>
</nav>
...
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
</Routes>
在这种情况下,上述两个Link会连接到/dashboard/invoices
和 /dashboard/team
两个地址,这就是相对路由。
404路由
当没有一个URL匹配时,就会查找是否配置了通用路由path="*"
,他可以匹配任何形式的路由,当没有更精确的路由匹配时,就进入该路由的配置,该路由的优先级是最低的。
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
多个路由组
一个Routes
组件包裹的路由为一组,v6中你可以使用多个Routes
。每一个<Routes>
都是独立的,分别进行路由匹配,其都是相对于跟路由的一个链接。
function App() {
return (
<div>
<Sidebar>
<Routes>
<Route path="/" element={<MainNav />} />
<Route
path="dashboard"
element={<DashboardNav />}
/>
</Routes>
</Sidebar>
<MainContent>
<Routes>
<Route path="/" element={<Home />}>
<Route path="about" element={<About />} />
<Route path="support" element={<Support />} />
</Route>
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</MainContent>
</div>
);
}
多级Routes时注意事项
当子组件也使用了Routes
时,需要在其上一级父路由后边追加/*
,追加的*
的层级数取决于你子路由的层级数。
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
// 不加*会匹配不到Dashboard中的子路由
<Route path="dashboard/*" element={<Dashboard />} />
</Routes>
);
}
function Dashboard() {
return (
<div>
<p>Look, more routes!</p>
<Routes>
<Route path="/" element={<DashboardGraphs />} />
<Route path="invoices" element={<InvoiceList />} />
</Routes>
</div>
);
}
常用配置介绍完毕了。源文档的Tutorial感觉与本章基本类似,我打算跳过他,下一章讲解FAQs。
转载自:https://juejin.cn/post/7100479939694034952