react router 级联路由_手把手教你快速升级将React Router v5 路由器更新到v6

b443cd124aeb48b68e66dee7c58d0387

图片来自网络

1 引言

React Router v6 alpha 版本发布了,本周通过 A Sneak Peek at React Router v6 这篇文章分析一下带来的改变。

React Router v6:https://github.com/ReactTraining/react-router

A Sneak Peek at React Router v6 :https://alligator.io/react/react-router-v6/

2 概述

更名为

一个不痛不痒的改动,使 API 命名更加规范。

// v5import { BrowserRouter, Switch, Route } from "react-router-dom";function App() {  return (      );}

在 React Router v6 版本里,直接使用 Routes 替代 Switch:

// v6import { BrowserRouter, Routes, Route } from "react-router-dom";function App() {  return (    } />        } />        );}

升级

在 v5 版本立,想要给组件传参数是不太直观的,需要利用 RenderProps 的方式透传 routeProps:

import Profile from './Profile';// v5 (      )}/>// v6} />} />

而在 v6 版本中,render 与 component 方案合并成了 element 方案,可以轻松传递 props 且不需要透传 roteProps 参数。

更方便的嵌套路由

在 v5 版本中,嵌套路由需要通过 useRouteMatch 拿到 match,并通过 match.path 的拼接实现子路由:

// v5import {  BrowserRouter,  Switch,  Route,  Link,  useRouteMatch} from "react-router-dom";function App() {  return (      );}function Profile() {  let match = useRouteMatch();  return (    
My Profile
);}

在 v6 版本中省去了 useRouteMatch 这一步,支持直接用 path 表示相对路径:

// v6import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";// Approach #1function App() {  return (    } />        } />        );}function Profile() {  return (    
My Profile } /> } />
);}// Approach #2// You can also define all// in a single placefunction App() { return ( } /> }> } /> } /> );}function Profile() { return (
My Profile
);}

注意 Outlet 是渲染子路由的 Element。

useNavigate 替代 useHistory

在 v5 版本中,主动跳转路由可以通过 useHistory 进行 history.push 等操作:

// v5import { useHistory } from "react-router-dom";function MyButton() {  let history = useHistory();  function handleClick() {    history.push("/home");  }  return Submit;}

而在 v6 版本中,可以通过 useNavigate 直接实现这个常用操作:

// v6import { useNavigate } from "react-router-dom";function MyButton() {  let navigate = useNavigate();  function handleClick() {    navigate("/home");  }  return Submit;}

react-router 内部对 history 进行了封装,如果需要 history.replace,可以通过 { replace: true } 参数指定:

// v5history.push("/home");history.replace("/home");// v6navigate("/home");navigate("/home", { replace: true });

更小的体积 8kb

由于代码几乎重构,v6 版本的代码压缩后体积从 20kb 缩小到 8kb。

3 精读

react-router v6 源码中有一段比较核心的理念,笔者拿出来与大家分享,对一些框架开发是大有裨益的。我们看 useRoutes 这段代码节选:

export function useRoutes(routes, basename = "", caseSensitive = false) {  let {    params: parentParams,    pathname: parentPathname,    route: parentRoute  } = React.useContext(RouteContext);  if (warnAboutMissingTrailingSplatAt) {    // ...  }  basename = basename ? joinPaths([parentPathname, basename]) : parentPathname;  let navigate = useNavigate();  let location = useLocation();  let matches = React.useMemo(    () => matchRoutes(routes, location, basename, caseSensitive),    [routes, location, basename, caseSensitive]  );  // ...  // Otherwise render an element.  let element = matches.reduceRight((outlet, { params, pathname, route }) => {    return (          );  }, null);  return element;}

可以看到,利用 React.Context,v6 版本在每个路由元素渲染时都包裹了一层 RouteContext。

拿更方便的路由嵌套来说:

在 v6 版本中省去了 useRouteMatch 这一步,支持直接用 path 表示相对路径。

这就是利用这个方案做到的,因为给每一层路由文件包裹了 Context,所以在每一层都可以拿到上一层的 path,因此在拼接路由时可以完全由框架内部实现,而不需要用户在调用时预先拼接好。

再以 useNavigate 举例,有人觉得 navigate 这个封装仅停留在形式层,但其实在功能上也有封装,比如如果传入但是一个相对路径,会根据当前路由进行切换,下面是 useNavigate 代码节选:

export function useNavigate() {  let { history, pending } = React.useContext(LocationContext);  let { pathname } = React.useContext(RouteContext);  let navigate = React.useCallback(    (to, { replace, state } = {}) => {      if (typeof to === "number") {        history.go(to);      } else {        let relativeTo = resolveLocation(to, pathname);        let method = !!replace || pending ? "replace" : "push";        history[method](relativeTo, state);      }    },    [history, pending, pathname]  );  return navigate;}

可以看到,利用 RouteContext 拿到当前的 pathname,并根据 resolveLocation 对 to 与 pathname 进行路径拼接,而 pathname 就是通过 RouteContext.Provider 提供的。

巧用多层 Context Provider

很多时候我们利用 Context 停留在一个 Provider,多个 useContext 的层面上,这是 Context 最基础的用法,但相信读完 React Router v6 这篇文章,我们可以挖掘出 Context 更多的用法:多层 Context Provider。

虽然说 Context Provider 存在多层会采取最近覆盖的原则,但这不仅仅是一条规避错误的功能,我们可以利用这个功能实现 React Router v6 这样的改良。

为了更仔细说明这个特性,这里再举一个具体的例子:比如实现搭建渲染引擎时,每个组件都有一个 id,但这个 id 并不透出在组件的 props 上:

const Input = () => {  // Input 组件在画布中会自动生成一个 id,但这个 id 组件无法通过 props 拿到};

此时如果我们允许 Input 组件内部再创建一个子元素,又希望这个子元素的 id 是由 Input 推导出来的,我们可能需要用户这么做:

const Input = ({ id }) => {  return ;};

这样做有两个问题:

  1. 将 id 暴露给 Input 组件,违背了之前设计的简洁性。
  2. 组件需要对 id 进行拼装,很麻烦。

这里遇到的问题和 React Router 遇到的一样,我们可以将代码简化成下面这样,但功能不变吗?

const Input = () => {  return ;};

答案是可以做到,我们可以利用 Context 实现这种方案。关键点就在于,渲染 Input 但组件容器需要包裹一个 Provider:

const ComponentLoader = ({ id, element }) => {  {element};};

那么对于内部的组件来说,在不同层级下调用 useContext 拿到的 id 是不同的,这正是我们想要的效果:

const ComponentLoader = ({id,element}) => {  const { id: parentId } = useContext(Context)      {element}  }

这样我们在 Input 内部调用的 实际上拼接的实际 id 是 01,而这完全抛到了外部引擎层处理,用户无需手动拼接。

4 总结

React Router v6 完全利用 Hooks 重构后,不仅代码量精简了很多,还变得更好用了,等发正式版的时候可以快速升级一波。

另外从 React Router v6 做的这些优化中,我们从源码中挖掘到了关于 Context 更巧妙的用法,希望这个方法可以帮助你运用到其他更复杂的项目设计中。

转载链接:https://zhuanlan.zhihu.com/p/120712831

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用react-router-dom创建动态由非常简单。我们可以使用Route组件来定义由并在其中使用动态参数。以下是如何使用react-router-dom创建动态由的步骤: 1. 安装react-router-dom:使用以下命令安装react-router-dom。 ``` npm install react-router-dom ``` 2. 在项目中导入所需的组件: ```javascript import React from "react"; import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; ``` 3. 创建由并在其中使用动态参数: ```javascript <Router> <Switch> <Route exact path="/" component={Home} /> <<Route exact path="/users/:id" component={User} /> </Switch> </Router> ``` 上面的代码中,我们定义了一个具有动态参数的由。用户可以通过访问URL `/users/:id` 来访问该由。其中,`:id` 是一个动态参数,它将被替换为用户请求的实际值。 4. 在组件中获取动态参数: 在上面的代码中,我们定义了一个名为`User`的组件。我们可以在该组件中通过`props.match.params.id`来获取动态参数的值。以下是一个简单的示例: ```javascript import React from "react"; const User = (props) => { return <h1>User ID: {props.match.params.id}</h1>; }; export default User; ``` 在上面的代码中,我们使用`props.match.params.id`来获取动态参数的值,并将其显示在页面上。 这就是使用react-router-dom创建动态由的基本步骤。你可以根据你的具体需求进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值