[Bonus 系列] - 来看看 React Router v6 有什麽新功能?和 v5 有哪些地方不同?

在 2021 年 11 月初,React Router 正式释出 v6 版本,身为 React 开发者已经按捺不住好奇心,想看看这个版本究竟增加了什麽功能?修改了什麽语法?因此就有了这篇文章的诞生。而在这篇文章中,我会介绍 v6 的一些新功能和 v5 有哪些地方不同。

1. Switch 元件被 Routes 取代,传递 props 元件的方式也做了调整,出现了新的 props,element

v5:

<Switch>
  <Route path="/about">
    <About />
  </Route>
  <Route path="/topics">
    <Topics />
  </Route>
  <Route path="/">
    <Home />
  </Route>
</Switch>

v6:

<Routes>
  <Route path="/about" element={<About />} />
  <Route path="/topics" element={<Topics />} />
  <Route path="/" element={<Home />} />
</Routes>

2. Router 变聪明了,不用再加上 exact

在过去,需透过 exact 设定完全符合 url 时才会显现指定的元件内容,现在 Router 会抓取最相近的 url 去呈现对应的元件。

<Routes>
  <Route path="/about" element={<About />} />
  <Route path="/topics" element={<Topics />} />
  <Route path="/topics/:topic" element={<SpecTopics />} />
  <Route path="/" element={<Home />} />
</Routes>

3. NavLink 的 activeClassName prop 被移除

React Router v5 的官网有提到 activeClassName 被移除,v6 直接用 className 去判定即可。

<NavLink className={(navData) => navData.isActive ? "active" : "" } to="/about" />

4. 简化巢状路由、推出 Outlet API

之前的版本需要搭配 useRouteMatch 去组出巢状路由,现在可以直接写上想要的子路由上去。

v5:

export default function Topics() {
  let match = useRouteMatch();
  console.log(match);

  return (
    <div>
      <ul>
        <li>
          <Link to={`${match.url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/props-v-state`}>Props v. State</Link>
        </li>
      </ul>

      <Switch>
        <Route path={`${match.path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={match.path}>
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}

v6:

export default function Topics() {
  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to="components">Components</Link>
        </li>
        <li>
          <Link to="props-v-state">Props v. State</Link>
        </li>
      </ul>

      <Routes>
        <Route path=":topicId" element={<Topic />} />
        <Route path="*" element={<h3>Please select a topic.</h3>} />
      </Routes>
    </div>
  );
}

另外在 path 有没有加上 '/' 结果都是一样的:

// 路由为 `/hobby/favorite`
<Route
  path="favorite"
  element={<FavoriteHobbyListBody />}
/>

// 路由也为 `/hobby/favorite`
<Route
  path="/favorite"
  element={<FavoriteHobbyListBody />}
/>

Outlet api 也是建立巢状路由的一大利器,以下为官方提供的范例。

在 Dashboard 元件内部,会根据路由 /messages/tasks<Outlet /> 的地方呈现对应的元件 <DashboardMessages /> or <DashboardTasks>

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      {/* This element will render either <DashboardMessages> when the URL is
          "/messages", <DashboardTasks> at "/tasks", or null if it is "/"
      */}
      <Outlet />
    </div>
  );
}

function App() {
  return (
    <Routes>
      <Route path="/" element={<Dashboard />}>
        <Route
          path="messages"
          element={<DashboardMessages />}
        />
        <Route path="tasks" element={<DashboardTasks />} />
      </Route>
    </Routes>
  );
}

5. useNavigate 代替了 useHistory

useNavigate 的第一个参数可以是路由或是数字,代表前进或回去的页数。

范例1:

v5:

import { useHistory } from "react-router-dom";

const News = () => {
  let history = useHistory();

  return (
    <>
      <button onClick={()=> history.push("/home")}>Home</button>
    </>
  );
}

v6:

import { useNavigate } from "react-router-dom";

const News = () => {
  let navigate = useNavigate();

  return (
    <>
      <button onClick={()=> navigate('/home')}>Home</button>
    </>
  );
}

范例2:

v5:

import { useHistory } from "react-router-dom";

function Exchanges() {
  const { go, goBack, goForward } = useHistory();

  return (
    <>
      <button onClick={() => go(-2)}>
        2 steps back
      </button>
      <button onClick={goBack}>1 step back</button>
      <button onClick={goForward}>1 step forward</button>
      <button onClick={() => go(2)}>
        2 steps forward
      </button>
    </>
  );
}

v6:

import { useNavigate } from "react-router-dom";

function Exchanges() {
  const navigate = useNavigate();

  return (
    <>
      <button onClick={() => navigate(-2)}>
        2 steps back
      </button>
      <button onClick={() => navigate(-1)}>1 step back</button>
      <button onClick={() => navigate(1)}>
        1 step forward
      </button>
      <button onClick={() => navigate(2)}>
        2 steps forward
      </button>
    </>
  );
}

除了 useNavigate hook 外,v6 也提供了 元件。

6. useRoutes 代替 react-router-config

useRoutes 官网说明

import React from "react";
import { useRoutes } from "react-router-dom";

const App = () => {
  let element = useRoutes([
    {
      path: "/",
      element: <Dashboard />,
      children: [
        {
          path: "messages",
          element: <DashboardMessages />
        },
        { path: "tasks", element: <DashboardTasks /> }
      ]
    },
    { path: "team", element: <AboutPage /> }
  ]);

  return element;
}

7. 其他功能

除了上述几点之外,当然还有其他的变更,像是 React Router v6 整个 bundle size 缩小,更加轻量,不过以上就举比较常用的几点来说明,想了解更深入可以点击我推荐的 youtube 影片或是到官网去阅读文件罗!


最後,放上在铁人赛 Day19 文章中介绍 React Router v5 的范例程序码和修改後的 v6 版本程序码范例提供给读者做比较,不过范例中并没有将文中的全部功能都使用上去,读者可以自行练习看看哩!

v5 版本 codesandbox 范例
v6 版本 codesandbox 范例


<<:  2021-11-24 盘势分析

>>:  (ISC)² 道德准则

Day 22 Android Unit Test

今年的疫情蛮严重的,希望大家都过得安好, 希望疫情快点过去,能回到一些线下技术聚会的时光~ 今天目标...

【CSS】【Bootstrap】让图片满版的object-fit

【前言】 本系列为个人前端学习之路的学习笔记,在过往的学习过程中累积了很多笔记,如今想藉着IT邦帮忙...

Day 22 来写一个简单e2e测试

今天我们来写一个简单的form来当作测试吧,首先我们刻出一个简单的画面 const App: FC ...

[Day14] 测试与迭代

现在,基於我们现有的初始对话流与打造完成的语音应用程序。 来试着让它变得更好! 现在我们进入设计对...

EP15 - 中场闲聊,所以我说大家的 DevOps 和 CI/CD 呢?

ㄟ ,那个新出来的工具好酷喔 TeamsCity 和 Azure DevOps 好像很潮 拜托,G...