React-Router6路由新特性(React-Router4/5和React-Router6对比总结)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

简介

React-Router6出来有一段时间了,笔者感觉v6版的路由比v4/v5更好用了,不知道你们有没有这样的感觉,特别是在路由嵌套和通过配置文件渲染路由这一块,简直不要太爽。

今天我们对比 React-Router4/5来总结下React-Router6新特性,希望能让小伙伴们快速上手。

<Switch/>替换为<Routes/>

React-Router6版本移除了<Switch/>组件,并使用<Routes/>替换。

除了能替代<Switch/>组件的功能外,也做了一些改变,比如所有的<Route>都必须包裹在<Routes/>中,否则抛出错误。

React-Router4/5 使用 Switch

<Switch>
  <Route path="/home" component={Home} />
  <Route path="/login" component={Login} />
</Switch>
复制代码

React-Router6 使用 Routes

<Routes>
  <Route path="/home" element={<Home/>} />
  <Route path="/login" element={<Login/>} />
</Routes>
复制代码

<Route/>用法变化

React-Router6版本<Route/>组件变化较大,移除了componentrender属性,使用element属性替代,因此与之前的版本代码写法不兼容。

React-Router4/5 使用 component 收组件

<Route path="/home" component={Home} />
<Route path="/login" render={()=><Login/>}/>
复制代码

React-Router6 使用element接收组件

<Route path="/home" element={<Home/>} />
<Route path="/login" element={<Login/>} />
复制代码

重定向

React-Router6移除了<Redirect/>组件,但可以使用新增的<Navigate/>组件配合<Route/>组件实现重定向效果。

React-Router4/5 使用 Redirect

React-Router4/5中使用<Redirect/>组件。

<Route path='/user'}>
  {/* 支持绝对路径和相对路径 */}

  <Redirect to="/user/info" />
  
  {/* <Redirect to="info" /> */}
</Route>
复制代码

React-Router6 使用 Navigate

React-Router6需要<Navigate/>组件配合<Route/>组件实现重定向效果。

不清楚<Navigate/>组件的不要紧,笔者后面会细说。

{/* 支持绝对路径和相对路径 */}

<Route path="/user" element={<Navigate to="/user/info"/>}>


<Route path="/user" element={<Navigate to="../info"/>}>
复制代码

路由跳转

React-Router4/5中,我们的路由组件props会自动注入history、location、match三个路由相关属性,用来操作路由。

image.png

history用来进行路由跳转,location用来进行参数获取,match用来获取params参数。

并且不是路由组件想要使用路由属性也是可以的,类组件可以通过withRouter高阶组件包裹来实现,函数组件可以使用withRouter高阶组件包裹和useHistory、useRouteMatch、useParams三个hook来实现。

但是在React-Router6中,路由组件props不再自动注入history、location、match三个路由相关属性。并且移除了withRouter高阶组件。

React-Router4/5 使用 history、Link、NavLink

React-Router4/5可以使用history或者Link、NavLink来实现路由跳转。

// Link 字符串形式
<Link to="/courses" />

// Link 对象形式
<Link to={{ pathname: "/courses" }} />

// 函数组件 history形式
props.history.push("/courses")
props.history.replace("/courses")

// 函数组件 history hook 形式
const history = useHistory("/courses")
history.push("/courses")
history.replace("/courses")

// 类组件 history形式
this.props.history.push("/courses")
this.props.history.replace("/courses")
复制代码

React-Router6 使用 navigate、Link、NavLink

因为React-Router6中,路由组件props不再自动注入history所以我们不能再使用以前的方法来进行路由跳转了。

React-Router6新增了navigate用来代替history来进行路由的跳转,但是因为是hook的原因,所以只能用在函数组件中。

那类组件应该怎么办呢?我们可以看看官方的解释。官方也是推荐我们自己来封装withRouter高阶组件,以便类组件能使用路由。

import { useLocation, useNavigate, useParams } from "react-router";

// https://github.com/remix-run/react-router/issues/7256
export default function withRouter(Child) {
  return (props) => {
    const location = useLocation();
    const navigate = useNavigate();
    const params = useParams()

    return (
      <Child {...props} navigate={navigate} location={location} params={params} />
    );
  };
}
复制代码

我们用自己封装的withRouter高阶组件包裹我们的类组件就能在类组件props中获取到navigate、location、params对象啦。

后面的例子,笔者假设类组件都使用withRouter包裹了。

React-Router6使用navigate或者Link、NavLink来实现路由跳转。

// Link字符串形式
<Link to="/courses">courses</Link>

// Link对象
<Link to={{ pathname: "/courses"}}>courses</Link>

// 通过自定义withRouter 类组件通过this.props.navigate实现跳转。
// 函数组件通过props.navigate或useNavigate实现跳转。

// navigate 相对路径形式
navigate("../route3/75");

// navigate 绝对路径形式
navigate("/router/route3/75");

// navigate 对象形式
navigate({ pathname: "/courses"});
复制代码

路由参数传递和参数获取

前面已经说了React-Router6中,路由组件props不再自动注入history、location、match三个路由相关属性。所以获取参数的方式也发生了改变。

React-Router4/5路由参数传递

React-Router4/5支持params、search、state三种参数。

// Link 字符串形式
<Link to="/courses?sort=name" />

// Link 对象形式
<Link
  to={{
    pathname: "/courses",
    search: "?sort=name",
    hash: "#the-hash",
    state: { fromDashboard: true }
  }}
/>

// 以类组件为例 字符串形式
this.props.history.push("/courses?sort=name")

// 以类组件为例 对象形式
this.props.history.push({
  pathname: "/courses",
  search: "?sort=name",
  hash: "#the-hash",
  state: { fromDashboard: true }
})
复制代码

React-Router4/5路由获取参数

params需要我们配置动态路由,然后在路由的match.params或者在函数组件中使用useParams获取。

search笔者感觉非常不友好,没有vue-router中的query好用。如果要使用需要使用URLSearchParams封装。

state传递参数需要注意的一点是当使用HashRouter的时候,页面刷新state参数会丢失。

// 类组件
this.props.location.search // 获取search参数
this.props.location.state // 获取state参数
this.props.match.params // 获取params

// 函数组件
props.location.search // 获取search参数
props.location.state // 获取state参数
props.match.params // 获取params

// 函数组件 hook形式
const location = useLocation()
const params = useParams()
location.search // 获取search参数
location.state // 获取state参数
match.params // 获取params
复制代码

React-Router6 路由传递参数

React-Router6使用navigate来进行路由的跳转和传参。

前面已经说了React-Router6路由组件props不再自动注入history

在类组件需要我们单独封装withRouter组件才能在类组价获取navigate

在函数组件我们使用useNavigate获取navigate

React-Router6传参需要注意两个点:

  1. 传递state不再是对象里面传递了,而是单独传递,这是和React-Router4/5的区别。
  2. navigate跳转路径支持相对路径模式,类似我们终端操作。
// Link字符串形式
<Link to="route1?name=randy1#hash1" state={{ fromDashboard: 1 }}>route1</Link>

// Link对象
<Link to={{ pathname: "route3/73",search: "?name=randy2",hash: "#hash2",}} state={{ fromDashboard: 1 }}>route1</Link>

// navigate 相对路径形式
navigate("../route3/75", { state: { fromDashboard: "navigate state" } });
// navigate 绝对路径形式
navigate("/router/route3/75", {
  state: { fromDashboard: "navigate state" },
  replace: true,
});
复制代码

React-Router6 路由获取参数

params需要我们配置动态路由,在类组价我们可以使用自己封装的withRouter高阶组件通过params获取。在函数组件中使用useParams获取。

search在类组价我们可以使用自己封装的withRouter高阶组件通过location获取。在函数组件可以使用useSearchParamsuseLocation获取。

state在类组价我们可以使用自己封装的withRouter高阶组件通过location获取。在函数组件可以使用useLocation获取。需要注意当使用HashRouter的时候,页面刷新state参数会丢失。

// 类组件
this.props.location.search // 获取search参数
this.props.location.state // 获取state参数
this.props.params // 获取params

// 函数组件
props.location.search // 获取search参数
props.location.state // 获取state参数
props.params // 获取params

// 函数组件 hook形式
const location = useLocation()
const params = useParams()
location.search // 获取search参数
location.state // 获取state参数
match.params // 获取params

let [searchParams, setSearchParams] = useSearchParams()// 获取search参数
searchParams.get(xxx)
复制代码

路由嵌套

使用过vue-router的同学是不是感觉react-router的路由嵌套非常难用,至少笔者有这样的感觉。

但是React-Router6版本对路由嵌套这块的改动是比较大的。改版后和vue-router的使用很像。

React-Router4/5 父子都需要定义路由

React-Router4/5中,子路由配置需要写到对应的子页面,让业务代码和路由逻辑混杂在一起,非常不友好。并且子路由需要补全父路由路径,哇崩溃。

// App.jsx 父组件
<Switch>
  <Route path="/home" component={<Home/>} />
  <Route path="/user" component={<User/>} />
</Switch>

// User.jsx 子组件
<Switch>
  <Route path="/user/info" component={<UserInfo/>} />
  <Route path="/user/detail" component={<UserDetail/>} />
</Switch>
复制代码

React-Router6 支持根组件统一定义路由

他来啦,他来啦。在React-Router6中,子路由配置不强制写到对应的子页面,有新的使用方式,非常友好。并且子路由也无需补全父路由路径。是不是爽到爆。

笔者的感觉就是跟vue-router靠齐了。

React-Router6中,以前的嵌套写法还是没有移除,只是做了些许改动。

需要注意的有两点:

  1. /user/*,也就是需要在父组件路由下带上*,表示这是一个父路由。
  2. 子路由不再需要补充父路由路径,比如下面的info、detail,会自动生成/user/info、/user/detail
// App.jsx 父组件
<Routes>
  <Route path="/home" element={<Home/>} />
  <Route path="/user/*" element={<User/>} />
</Routes>

// User.jsx 子组件
<Routes>
  <Route path="info" element={<UserInfo/>} />
  <Route path="detail" element={<UserDetail/>} />
</Routes>
复制代码

React-Router6全新写法。

  1. 子路由不再需要写到子路由页面,全部在外面配置。只需要写上<Outlet />即可,子路由匹配上到时会渲染在这里。类似vue里面的router-view
  2. 子路由不再需要补充父路由路径,比如下面的info、detail,会自动生成/user/info、/user/detail
// App.jsx 父组件
<Routes>
  <Route path="/home" element={<Home/>} />
  <Route path="/user" element={<User/>}>
    <Route path="info" element={<UserInfo/>} />
    <Route path="detail" element={<UserDetail/>} />
  </Route>
</Routes>

// User.jsx 子组件
<Outlet />
复制代码

当然子路由写全路径也是支持的,但是没必要。

// App.jsx 父组件
<Routes>
  <Route path="/home" element={<Home/>} />
  <Route path="/user" element={<User/>}>
    <Route path="/user/info" element={<UserInfo/>} />
    <Route path="/user/detail" element={<UserDetail/>} />
  </Route>
</Routes>

// User.jsx 子组件
<Outlet />
复制代码

通过配置渲染路由

React-Router4/5中,如果想实现类似vue中的routes通过配置来渲染路由是很麻烦的。但在React-Router6使用useRoutes方法可以很方便的实现。

React-Router4/5 使用 map 或 react-router-config

首先我们需要定义routes

// routerConfig/routes.js

import Parent from "./Parent";
import Child1 from "./Child1";
import Child2 from "./Child2";

const routes = [
  {
    component: Parent,
    path: "/parent",
    routes: [
      {
        path: "/parent/child1",
        exact: true,
        component: Child1,
      },
      {
        path: "/parent/child2",
        component: Child2,
      },
    ],
  },
];

export default routes;
复制代码

在根组件,我们需要自己使用map遍历渲染,并把子路由通过routes传递下去。

import routes from "./routerConfig/routes";

<BrowserRouter>
  <Switch>
    {routes.map((route) => {
      return (
        <Route
          path={route.path}
          key={route.path}
          render={(props) => {
            return (
              <route.component
                {...props}
                routes={route.routes}
              ></route.component>
            );
          }}
        ></Route>
      );
    })}
  </Switch>
</BrowserRouter>
复制代码

在子组件我们还需要继续使用map遍历渲染。

// Parent.js

<Switch>
  {this.props.routes.map((route) => {
    return (
      <Route
        path={route.path}
        key={route.path}
        render={(props) => {
          return (
            <route.component
              {...props}
              routes={route.routes}
            ></route.component>
          );
        }}
      ></Route>
    );
  })}
</Switch>
复制代码

是不是很复杂,但是我们可以使用react-router-config插件进行简化。

首先安装react-router-config

npm i react-router-config
复制代码

在根组件,我们只需要使用renderRoutes方法就可以了。

import { renderRoutes } from "react-router-config";
import routes from "./routerConfig/routes";

{renderRoutes(routes)}
复制代码

在子组件我们继续使用renderRoutes

// Parent.js

{renderRoutes(this.props.routes)}
复制代码

相对自己写遍历是不是方便了很多呢?其实react-router-config插件内部实现跟笔者上面写的差不多。

React-Router6 使用 useRoutes

React-Router6能更方便的实现。只需要使用useRoutes方法。

首先定义routes

// routerConfig/routes.js

import Parent from "./Parent";
import Child1 from "./Child1";
import Child2 from "./Child2";

const routes = [
  {
    element: <Parent></Parent>,
    path: "/parent",
    children: [
      {
        path: "child1",
        element: <Child1></Child1>,
      },
      {
        path: "child2",
        element: <Child2></Child2>,
      },
    ],
  },
];
复制代码

在根组件,我们只需要使用useRoutes方法就可以了。

这里需要注意useRouteshook,所以只能在函数组件或自定义组件使用。

import { useRoutes } from "react-router-dom";
import routes from "./routerConfig/routes";

function App() {
 return {useRoutes(routes)}
}
复制代码

在子组件我们继续使用Outlet渲染子组件就可以了。

// Parent.js

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

<Outlet />
复制代码

是不是很简单,感觉React路由这一块有点向Vue-Router靠齐了,从路由嵌套和配置式路由就能看出来,后面把路由钩子也更新出来就更爽啦。

后记

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!

猜你喜欢

转载自juejin.im/post/7103731196856631327