React Router v6 官方文档翻译 (一) ---- Installation && Quick Start

7,588

专栏说明:对于react-router v6版本,有同事反应缺少相对完整的中文文档,查看不方便。为了便于使用,作者对官网文档进行针对性翻译,便于v6版本更广泛的被使用。

安装

本文档介绍使用基于React框架的不同构建工具来配置React Router的一般方案。

基础安装

原文路径 reactrouter.com/docs/en/v6/…

大多数React工程项目都使用包管理器npm 或者 Yarn进行依赖管理。使用React Router前的第一件事情,就是基于你的包管理工具进行React Router的安装。

  1. npm
$ npm install react-router-dom@6
  1. yarn
$ yarn add react-router-dom@6
  1. pnpm
$ pnpm add react-router-dom@6

基于Create-react-app项目的安装

原文路径 reactrouter.com/docs/en/v6/…

当你在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项目的安装

原文路径 reactrouter.com/docs/en/v6/…

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标签全局安装

原文路径 reactrouter.com/docs/en/v6/…

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包含了很多小工具和组件,按需引入才是王道。所以还是推荐前三种安装方式。

快速开始(有一些与安装重复)

原文地址 reactrouter.com/docs/en/v6/…

引入

安装依赖,前边已经提到:

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。