React-Router v6 新特性解读及迁移指南

来自:奇舞周刊 04月11日

前言

18 年初, React Router 的主要开发人员创建一个名为 Reach Router 的轻量级替代方案。

原来是相互抗衡的,却没想 React Router 直接拿来合并(真香!)

目前 v6 已是测试最后一版,估计新的特性不出意外就是下面这些了:

    <Switch> 重命名为 <Routes><Route> 的新特性变更。

    嵌套路由变得更简单。

    useNavigate 代替 useHistory 。 新钩子 useRoutes 代替 react-router-config 。 大小减少:从 20kb8kb

1. <Switch> 重命名为 <Routes>

该顶级组件将被重命名。但是,其功能大部分保持不变(嗨,瞎折腾)。

// v5

<Switch>

<Route exact path="/"><Home /></Route>

<Route path="/profile"><Profile /></Route>

</Switch>


// v6

<Routes>

<Route path="/" element={<Home />} />

<Route path="profile/*" element={<Profile />} />

</Routes>

2. <Route> 的新特性变更

component/renderelement 替代

总而言之,简而言之。就是变得更好用了。

import Profile from './Profile';


// v5

<Route path=":userId" component={Profile} />

<Route

path=":userId"

render={routeProps => (

<Profile routeProps={routeProps} animate={true} />

)}

/>


// v6

<Route path=":userId" element={<Profile />} />

<Route path=":userId" element={<Profile animate={true} />} />

3. 嵌套路由变得更简单

具体变化有以下:

<Route children> 已更改为接受子路由。 比 <Route exact><Route strict> 更简单的匹配规则。 <Route path> 路径层次更清晰。

3.1 简化嵌套路由定义

v5 中的嵌套路由必须非常明确定义,且要求在这些组件中包含许多字符串匹配逻辑(活久见啊,终于意识到这个问题了。)

且看之前的处理:

// v5

import {

BrowserRouter,

Switch,

Route,

Link,

useRouteMatch

} from 'react-router-dom';


function App() {

return (

<BrowserRouter>

<Switch>

<Route exact path="/" component={Home} />

<Route path="/profile" component={Profile} />

</Switch>

</BrowserRouter>

);

}


function Profile() {

let { path, url } = useRouteMatch();


return (

<div>

<nav>

<Link to={`${url}/me`}>My Profile</Link>

</nav>


<Switch>

<Route path={`${path}/me`}>

<MyProfile />

</Route>

<Route path={`${path}/:id`}>

<OthersProfile />

</Route>

</Switch>

</div>

);

}

而在 v6 中,你可以删除字符串匹配逻辑。不需要任何 useRouteMatch()

// v6

import {

BrowserRouter,

Routes,

Route,

Link,

Outlet

} from 'react-router-dom';


function App() {

return (

<BrowserRouter>

<Routes>

<Route path="/" element={<Home />} />

<Route path="profile/*" element={<Profile/>} />

</Routes>

</BrowserRouter>

);

}


function Profile() {

return (

<div>

<nav>

<Link to="me">My Profile</Link>

</nav>


<Routes>

<Route path="me" element={<MyProfile />} />

<Route path=":id" element={<OthersProfile />} />

</Routes>

</div>

);

}

当然,还有更酸爽的操作,直接在路由里定义 <Route><Route> ,然后用接下来的一个新 APIOutlet

3.2 新 API : Outlet

这玩意儿,像极了 {this.props.children} ,具体用法看以下例子:

function App() {

return (

<BrowserRouter>

<Routes>

<Route path="/" element={<Home />} />

<Route path="profile" element={<Profile />}>

<Route path=":id" element={<MyProfile />} />

<Route path="me" element={<OthersProfile />} />

</Route>

</Routes>

</BrowserRouter>

);

}


function Profile() {

return (

<div>

<nav>

<Link to="me">My Profile</Link>

</nav>

{/*

将直接根据上面定义的不同路由参数,渲染<MyProfile />或<OthersProfile />

*/}

<Outlet />

</div>

)

}

3.3 多个 <Routes />

以前,我们只能 在 React App 中使用一个 Routes 。但是现在我们可以在 React App 中使用多个路由,这将帮助我们基于不同的路由管理多个应用程序逻辑。

import React from 'react';

import { Routes, Route } from 'react-router-dom';


function Dashboard() {

return (

<div>

<p>Look, more routes!</p>

<Routes>

<Route path="/" element={<DashboardGraphs />} />

<Route path="invoices" element={<InvoiceList />} />

</Routes>

</div>

);

}


function App() {

return (

<Routes>

<Route path="/" element={<Home />} />

<Route path="dashboard/*" element={<Dashboard />} />

</Routes>

);

}

4. 用 useNavigate 代替 useHistory

从一目了然改到双目失明。。。

总感觉 React Router 团队有点儿戏。。。

// v5

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


function MyButton() {

let history = useHistory();

function handleClick() {

history.push('/home');

};

return <button onClick={handleClick}>Submit</button>;

};

现在, history.push() 将替换为 navigation()

// v6

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


function MyButton() {

let navigate = useNavigate();

function handleClick() {

navigate('/home');

};

return <button onClick={handleClick}>Submit</button>;

};

history 的用法也将被替换成:

// v5

history.push('/home');

history.replace('/home');


// v6

navigate('/home');

navigate('/home', {replace: true});

强行达成共识

5. 新钩子 useRoutes 代替 react-router-config

感觉又是一波强行 hooks ,但还是相对于之前简洁了一些。。。

function App() {

let element = useRoutes([

{ path: '/', element: <Home /> },

{ path: 'dashboard', element: <Dashboard /> },

{ path: 'invoices',

element: <Invoices />,

children: [

{ path: ':id', element: <Invoice /> },

{ path: 'sent', element: <SentInvoices /> }

]

},

// 重定向

{ path: 'home', redirectTo: '/' },

// 404找不到

{ path: '*', element: <NotFound /> }

]);

return element;

}

6. 大小减少:从 20kb8kb

React Router v6 给我们带来方便的同时,还把包减少了一半以上的体积。。。

感觉可以去看一波源码了。。。

关于奇舞周刊

《奇舞周刊》是360公司大前端团队「 奇舞团 」运营的前端技术社区。关注公众号后,直接发送链接到后台即可给我们投稿。