阅读 1479

React-Router v6新特性(react-router-dom文档)

React-Router v6稳定版本已在近期发布,相较前两个主版本(v5和v4, v5并没有做破坏性更改)带来了破坏性的api,v6路由功能搭配一系列的hooks将变得更加强大,我们先一睹为快。

  1. Routes替换Switch

    v5版本中使用Switch来匹配命中路由的组件

    // v5 function App() {   return (     <BrowserRouter>       <Switch>         <Route exact path="/">           <Home />         </Route>         <Route path="/about">           <About />         </Route>         <Route path="/users/:id" children={<User />} />       </Switch>     </BrowserRouter>   ); } 复制代码

    在v5中,Switch担任路由匹配的核心角色,它会遍历查找自己的子元素,基于v5的路由匹配算法会渲染第一个命中路由的组件。

    // v6 function App() {    return (      <BrowserRouter>        <Routes> // 使用Routes替换 Switch            <Route path="/Home" element={<Home />} />           <Route path="/user" element={<Users />} />        </Routes>      </BrowserRouter>    ) } 复制代码

    v6版本使用Routes替换Switch组件,语义上更贴近其子元素RouteRoutes 中实现了全新的路由查找算法,许多新特性大多基于此算法实现。

  2. 规范Route的渲染属性

    v5中提供component、render、children三种方式渲染组件

    // v5 function App() {   return (     <BrowserRouter>       <Switch>         // component渲染         <Route exact path=":userId" component={User} />         <Route            path=":userId"            // render函数渲染           render={routeProps => (             <User routeProps={routeProps} animate={true} />           )}          />         <Route           path=":userId"           // children渲染           children={({ match }) => (             match ? (               <User match={match} animate={true} />             ) : (               <NotFound />             )           )}         />         <Route           path=":userId"           children={<User animate={true} />}         />       </Switch>     </BrowserRouter>   ); } 复制代码

    v5的Route可以接收component作为渲染组件,看起来很简单,但我们无法给该组件(User)传递自定义属性,因此我们可以使用render来弥补component的缺陷,除此之外v5版本还提供children属性,当children是一个函数时,接收路由匹配的上下文数据,实际上功能和render相同。v6版本将Route的三个属性统一规范成element

    // v6 function App() {    return (      <BrowserRouter>        <Routes>           <Route path="/user" element={<User />} />           <Route path="/user/1" element={<User animate={true} />} />        </Routes>      </BrowserRouter>    ) }  // 结合v6提供的hooks,可以获取路由相关数据 function User({ animate }) {   let params = useParams();   let location = useLocation(); } 复制代码

    element的类型为React.ReactNode,可以传入Suspense实现路由懒加载。v6在支持原有路由功能的基础上,通过规范、减少组件api来减轻开发者使用的负担,最终让开发体验得到提升。

  3. 手动排序 vs 智能匹配

    由于v5中的路由算法是渲染第一个命中的路由组件,开发者在使用时需要进行手动排序来展示组件优先级。v6中的路由匹配算法更加智能、强大,会通过计算比较返回优先级高的路由组件。通过一个例子看他们之间的区别。

        <Route path="/user/:id" component={User} />     <Route path="/user/new" component={NewUser} /> 复制代码

    我们定义了两个路由,实际上访问大多数/user路径没有很大的歧义,但当页面访问/user/new时,此时同时命中了这两个路由,那么最终会渲染哪个组件? 在v5中,最终渲染是先定义的路由组件(User),即先定义,先渲染(符合之前说的v5路由算法返回第一个匹配的组件),所以开发者需要手动对路由进行排序控制组件渲染的优先级。但这并不符合我们的页面渲染预期。 在v6中,一切变的更加自动和智能,开发者不需要手动维护路由组件的顺序,而是交给v6路由匹配算法自动选择渲染,那么v6具体是怎样实现的呢?简单的讲,在v6内部,会对每个路径进行分割,对路径中的各个部分累计打分排名,分数越高,则优先渲染,其打分方法如下:

    // 匹配路由动态部分, 如/:id const paramRe = /^:\w+$/; // 动态路由部分分值 const dynamicSegmentValue = 3; // index子路由分值 const indexRouteValue = 2; // 空路由部分分值 const emptySegmentValue = 1; // 静态路由部分分值 const staticSegmentValue = 10; // 当路径中存在*时分值 const splatPenalty = -2; const isSplat = (s: string) => s === "*";  function computeScore(path: string, index: boolean | undefined): number {   // 分割路径成数组   let segments = path.split("/");   let initialScore = segments.length;   if (segments.some(isSplat)) {     initialScore += splatPenalty;   }   if (index) {    initialScore += indexRouteValue;  }   return segments    .filter(s => !isSplat(s))    .reduce(      (score, segment) =>        score +        (paramRe.test(segment)          ? dynamicSegmentValue          : segment === ""          ? emptySegmentValue          : staticSegmentValue),      initialScore    ); } 复制代码

    观察这个方法,可以看出越是动态的路由它的分数往往越低,例如,当路径中存在*时,它的初始分数就相对较低,同时动态路由部分分值(dynamicSegmentValue: 3)比静态路由部分分值(staticSegmentValue: 10)低了许多。回到上面的例子,在访问/user/new路径时,v6会计算/user/:id/user/new路由分数,由于/user/new是静态路由分数会高于/user/:id,因此v6中会渲染NewUser组件。

  4. 相对路径的Link和Route v5嵌套路由里需要拼接绝对路径来渲染组件的子路由,例如

    // v5 function App() {  return (    <BrowserRouter>      <Switch>        <Route exact path="/">          <Home />        </Route>        <Route path="/users">          <Users />        </Route>      </Switch>    </BrowserRouter>  ); }  function Users() {   // 当子组件渲染嵌套路由时,需要通过useRouteMatch获得match对象,开发者基于match对象拼接绝对路径供Link和Route组件使用。   let match = useRouteMatch();    return (     <div>       <nav>         <Link to={`${match.url}/me`}>My Profile</Link>       </nav>        <Switch>         <Route path={`${match.path}/me`}>           <OwnUserProfile />         </Route>         <Route path={`${match.path}/:id`}>           <UserProfile />         </Route>       </Switch>     </div>   ); } 复制代码

    在v6中,开发者使用Link或Route时只需定义相对父路由的相对路径即可,v6内部会为我们自动拼接全路径。

    // v6 function App() {   return (     <BrowserRouter>       <Routes>         <Route path="/" element={<Home />} />         <Route path="users/*" element={<Users />} />       </Routes>     </BrowserRouter>   ); } function Users() {   return (     <div>       <nav>         <Link to="me">My Profile</Link>       </nav>       <Routes>         <Route path=":id" element={<UserProfile />} />         <Route path="me" element={<OwnUserProfile />} />       </Routes>     </div>   ); } 复制代码

  5. Outlet组件

    v6带来了Outlet组件,用于渲染当前路由下的子路由组件,我们对第四点中的v6版本代码做一下转换:

    // v6 function App() {   return (     <BrowserRouter>       <Routes>         <Route path="/" element={<Home />} />         <Route path="users" element={<Users />}>           <Route path="me" element={<OwnUserProfile />} />           <Route path=":id" element={<UserProfile />} />         </Route>       </Routes>     </BrowserRouter>   ); }  function Users() {   return (     <div>       <nav>         <Link to="me">My Profile</Link>       </nav>       // 当访问/users/me 或者 /users/id时,子路由会被渲染       <Outlet />     </div>   ); } 复制代码

    通过Outlet可以将所有的路由(嵌套的子路由)配置合并在一起,可进行路由的统一管理,增加了代码可维护性。

  6. 一系列的Hooks

    实际上v5也提供了个别hooks,例如useHistoryuseLocation以及上文提到的useRouteMatch等,但v5版本核心组件还是基于类组件开发,随着React Hooks的发布,社区中的React类库开始向Hooks迁移,因此v5中的hooks相当于过渡使用。v6开始全面重写,拥抱Hooks函数组件以及TS,许多API也是利用组合的思想来拆分、包装代码,例如Routes则是useRoutes的包装,NavigateuseNavigate的包装,OutletuseOutlet的包装。

总结

React Router目前是React应用路由管理的最佳方案(没有之一), v6版本基于全新的路由算法带来强大的功能和hooks,开发体验大大增强????


作者:发多
链接:https://juejin.cn/post/7031455218097356831


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐