踩坑react-router v6

360 阅读4分钟

都说react-router东西不多,但是怎么说也是react生态中不可获取的一环,就算不去深入了解,那么起码得需要知道它的一些api和用法。毕竟也用了这么久,我自认为是会用的,但是v6版本的出现还是让我认清自己,不能只专注于表面,并且在新版本或者技术出现的时候,要给与一定的关注。

版本主要更改

1.改为变为

其实算是一个重命名,改动不大

2.属性更改

主要体现在component改成了element,并且需要用组件标签引入

// 之前的route
<Route path="/" component={Home}/>
// 现在的
<Route path="/" element={<Home />}/>

3. 删除了Redirect

原来的Redirect大家也都用习惯了,觉得很好用,但是v6把它给删了!!! 但是没事,我们还有其他方案。

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

<Routes>
   <Route path="*" element={<Navigate to="/home" />} />
</Routes>

4. 嵌套路由方式更改

4.1 路由嵌入方式更改,使用了新api Outlet

// routers.js
<Route path='/user' element={<user />}>
  <Route path='/list' element={<userList />} />
</Route>
// user页面内
import { Outlet } from 'react-router-dom'
const User = ()=>{
  return (
    <div>
      这是一级页面
      <Outlet/>
      这是二级页面的内容,展示匹配的路由页面组件内容
  	</div>
  )
}

4.2 嵌套编写方式

不需要再使用useRouteMatch去填充path了。

// 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的

// 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>
  );
}

4.3 可多次嵌套写switch(Routes)

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>
  );
}

5. 用useNavigate代替useHistory

也是类似于名字更改,不一样的是实例的方法也不太一样了

// v5
history.push('/home');
history.replace('/home');

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

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

再用react-router-config配合v6就发现报错了,现在v6用的是useRoutes,有点坑,我以为只是名字一改,结果大错特错,下面是我的踩坑过程

// v5 config用法
import { HashRouter } from "react-router-dom";
import { renderRoutes } from 'react-router-config'
import routers from "@/routers";

function App() {
  return (
    <HashRouter>
      {renderRoutes(routers)}
    </HashRouter>
  );
}

到了v6,改用useRoutes之后,我以为的↓

// 我以为的
import { HashRouter, useRoutes } from "react-router-dom";
import routers from "@/routers";

function App() {
  return (
    <HashRouter>
      {useRoutes(routers)}
    </HashRouter>
  );
}

结果当然报错了 image.png

我一看错误提示,必须在下面。。。

// 馊主意
import { HashRouter, useRoutes, Router } from "react-router-dom";
import routers from "@/routers";

function App() {
  return (
    <HashRouter>
      <Router>
      	{useRoutes(routers)}
      </Router>
    </HashRouter>
  );
}

这个更不用说,肯定还是错的,也是报一样的错误,虽然语义上理解没什么问题。于是我上github router项目上的api去看看用法,原来有实例,我照猫画虎得到如下

// 官方提供
import { useRoutes } from "react-router-dom";
import routers from "@/routers";

function App() {
  const rt = useRoutes(routers)
  return rt
}

结果竟然还是错的,一样的报错信息,没道理啊,后来还是百度出来了最终方案,看起来不少人碰到了这个坑

// 最终可行方案
import { HashRouter, useRoutes } from "react-router-dom";
import routers from "@/routers";

function Rout(){
	const rt = useRoutes(routers)
  return rt
}

function App() {
  return (
  	<HashRouter>
      <Rout/>
    </HashRouter>  
  )
}

终于成了

7. 包的大小减少了

从20kb到8kb,肯定有很多优化,很多奇思妙想吧

8. 路径格式变化

不再支持正则的匹配,而仅支持2种占位符:动态:id样式参数和*通配符。 并且会在匹配的时候默认忽略所有url末尾的/。 现在的link to的url,非常的类似cd

当前路径:"/users",则<Link to="me">将跳转<a href="/users/me">。
当前路径:"/users",则<Link to="../me">将跳转<a href="/me">。
//v5
当前路径:"/users",则<Link to="me">将跳转<a href="/me">。
当前路径:"/users/",则<Link to="me">将跳转<a href="/users/me">。

如何在6下使用懒加载

如何在上面的改造后再使用懒加载呢

// v5版本
// App.js
import { Suspense } from "react";
<Suspense fallback={renderLoader()}>{renderRoutes(routes)}</Suspense
  
// 路由表,router.js
import { lazy } from "react";
//...
  {
    path: '/',
    exact: true,
    component: lazy(() => import('@/pages/Home')),
  },

v6也可以这么做,但是就不能再使用useRoute了,需要自己手动去map路由表

// v6的循环map路由
<Routes>
 {
    routers.map((item, i) => {
      return (
        <Route key={i} path={item.path} element={
            <Suspense fallback={
                <div>路由懒加载...</div>
              }>
              <item.element />
            </Suspense>
          } />
      )
    })
  }
</Routes>

还有另外一种方式 三方包了------react-loadable

// 路由表router.js
import React from 'react'
import loadable from 'react-loadable' //引入这个loadable,使用这个来加载路由

const LoadingTip = () => <div>懒加载路由ing...</div>
// 如果你是js就直接无视这个: Array<Router>的类型限定
const router = [
  {
    path: '/',
    component: loadable({
      loader: () => import('@/views/home'), // 需要异步加载的路由
      loading: LoadingTip // 这是一个的提示
    })
  },
  {
    path: '/about',
    component: loadable({
      loader: () => import('@/views/about'),
      loading: LoadingTip
    })
  }
]

export default router
// APP.js
<Routes>
 {
    router.map((item, i) => {
      return (
        <Route
          key={i}
          path={item.path}
          element={ < item.component /> }
        />
      )
    })
  }
</Routes>

​ 参考:浅析React Router V6 useRoutes的使用 react-router仓库 React-Router v6 新特性解读及迁移指南 react-router-dom v6的路由懒加载形式,这里写了两种