likes
comments
collection
share

React-Router V6 使用详解(干货)

作者站长头像
站长
· 阅读数 15
Remix 团队在20206月发布了第一个V6.0.0-beta.0版本的React-Router,也预示着V6版本的正式开始,相比V5版本的确有很多方面的升级。
本文将结合V6特性和V5如何升级V6两方面来为大家详细讲解React-Router的使用。
(使用版本:[V6.0.2稳定版](https://github.com/remix-run/react-router/releases/tag/v6.0.2))

一、基本用法

React-Router的安装方法:

npm: $ npm install react-router-dom@6

yarn$ yarn add react-router-dom@6

目前官方从5开始已经放弃原有的react-router库,统一命名为react-router-dom
使用方法
React-Router本身在React开发中就是一个组件,因此在使用时基本遵循组件开发相关原则。这里采用create-react-app来创建一个基础的demo工程演示使用过程。
  1. 创建demo create-react-app my-first-react

    安装react-router组件

  2. 启用全局路由模式

    全局路由有常用两种路由模式可选:HashRouter 和 BrowserRouter HashRouter:URL中采用的是hash(#)部分去创建路由,类似www.example.com/#/ BrowserRouter:URL采用真实的URL资源 后续有文章会详细讲HashRouter的原理和实现,这里我们采用BrowserRouter来创建路由

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import reportWebVitals from './reportWebVitals';
    import { BrowserRouter } from 'react-router-dom';
    
    ReactDOM.render(
      <React.StrictMode>
        <BrowserRouter>
          <App />
        </BrowserRouter>
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    reportWebVitals();
    

    这样我们在yarn start 或者 npm run start的时候访问/就可以访问这个组件了,具体效果大家可以自行运行

  3. 路由功能 React-Router V6版本常用路由组件和hooks,其他不常用的大家可以看下官网的介绍

    组件名作用说明
    <Routers>一组路由代替原有<Switch>,所有子路由都用基础的Router children来表示
    <Router>基础路由Router是可以嵌套的,解决原有V5中严格模式,后面与V5区别会详细介绍
    <Link>导航组件在实际页面中跳转使用
    <Outlet/>自适应渲染组件根据实际路由url自动选择组件
    hooks名作用说明
    useParams返回当前参数根据路径读取参数
    useNavigate返回当前路由代替原有V5中的 useHistory
    useOutlet返回根据路由生成的element
    useLocation返回当前的location 对象
    useRoutes同Routers组件一样,只不过是在js中使用
    useSearchParams用来匹配URL中?后面的搜索参数
    基础使用示例

    App.js

    这里创建了两个组件Home和About,然后分别注册/和about,在每个页面还有Link来进行导航
    
    import './App.css';
    import { Routes, Route, Link } from "react-router-dom"
    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <Routes>
              <Route path="/" element={<Home />}></Route>
              <Route path="/about" element={<About />}></Route>
            </Routes>
          </header>
        </div>
      );
    }
    function Home() {
      return <div>
        <main>
          <h2>Welcome to the homepage</h2>
        </main>
        <nav>
          <Link to="/about">about</Link>
        </nav>
      </div>
    }
    function About() {
      return <div>
        <main>
          <h2>Welcome to the about page</h2>
        </main>
        <nav>
          <ol>
            <Link to="/">home</Link>
            <Link to="/about">about</Link>
          </ol>
    
        </nav>
      </div>
    }
    export default App;
    
    

    运行后 React-Router V6 使用详解(干货)

    React-Router V6 使用详解(干货)

    至此,一个最简单的路由demo就正常运行了。

  4. 嵌套路由

    嵌套路由是V6版本对之前版本一个较大的升级,采用嵌套路由会智能的识别

    function App() {
      return (
        <Routes>
          <Route path="user" element={<Users />}>
            <Route path=":id" element={<UserDetail />} />
            <Route path="create" element={<NewUser />} />
          </Route>
        </Routes>
      );
    }
    

    当访问 /user/123 的时候,组件树将会变成这样

    <App>
        <Users>
            <UserDetail/>
        </Users>
    </App>
    

    当访问/user/create的时候,组件树将变成这样

    <App>
       <Users>
           <NewUser/>
       </Users>
    </App>
    

    如果只是内部组件修改,也可以采用<Outlet/>来直接实现,如下所示

    function App() {
      return (
        <Routes>
          <Route path="user" element={<Users />}>
            <Route path=":id" element={<UserDetail />} />
            <Route path="create" element={<NewUser />} />
          </Route>
        </Routes>
      );
    }
    function Users() {
      return (
        <div>
          <h1>Users</h1>
          <Outlet />
        </div>
      );
    }
    
  5. index路由

    index属性解决当嵌套路由有多个子路由但本身无法确认默认渲染哪个子路由的时候,可以增加index属性来指定默认路由

    function App() {
      return (
        <Routes>
          <Route path="/" element={<Layout />}>
            <Route index element={<About />} />
            <Route path="user" element={<User />} />
            <Route path="about" element={<About />} />
          </Route>
        </Routes>
      );
    }
    

    这样当访问/的时候<Outlet/>会默认渲染About组件

  6. 路由通配符

    整个react-router支持以下几种通配符
    
    /groups
    /groups/admin
    /users/:id
    /users/:id/messages
    /files/*
    /files/:id/*
    
    

    注意,以下这些正则方式在V6里面是不支持的

    /users/:id?
    /tweets/:id(\d+)
    /files/*/cat.jpg
    /files-*
    

    这里的*只能用在/后面,不能用在实际路径中间

    关于NotFound类路由,可以用*来代替

    function App() {
      return (
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="dashboard" element={<Dashboard />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      );
    }
    
  7. 获取参数 useParams 和useSearchParams

    假设现有App路由

    function App() {
     return (
       <Routes>
         <Route path="user" element={<Users />}>
           <Route path=":id" element={<UserDetail />} />
           <Route path="create" element={<NewUser />} />
         </Route>
       </Routes>
     );
    }
    

    那么在UserDetail内部需要用useParams来获取对应的参数

    import { useParams } from "react-router-dom";
    
    export default function UserDetail() {
      let params = useParams();
      return <h2>User: {params.id}</h2>;
    }
    

    useSearchParams相对复杂,他返回的是一个当前值和set方法

    let [searchParams, setSearchParams] = useSearchParams();

    使用时可以用searchParams.get("id")来获取参数,同时页面内也可以setSearchParams({"id":2})来改变路由,这样当访问 http://URL/user?id=111 时就可以获取和设置路径

  8. useNavigate

    useNavigate是替代原有V5中的useHistory的新hooks,其用法和useHistory类似,整体使用起来更轻量,他的声明方式如下:

    declare function useNavigate(): NavigateFunction;
    
    interface NavigateFunction {
      (
        to: To,
        options?: { replace?: boolean; state?: State }
      ): void;
      (delta: number): void;
    }
    
      //js写法
      let navigate = useNavigate();
      function handleClick() {
        navigate("/home");
      }
      //组件写法
      function App() {
         return <Navigate to="/home" replace state={state} />;
      }
      //替代原有的go goBack和goForward
     <button onClick={() => navigate(-2)}>
        Go 2 pages back
      </button>
      <button onClick={() => navigate(-1)}>Go back</button>
      <button onClick={() => navigate(1)}>
        Go forward
      </button>
      <button onClick={() => navigate(2)}>
        Go 2 pages forward
      </button>
    

二、与V5的区别

1.用<Routes> children形式替代<Switch>

V5写法:

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

V6写法

 function App() {
   return (
     <Routes>
       <Route index path="/" element={<Home />} />
       <Route path="about" element={<About />} />
       <Route path="/users/:id" element={<User />} />
     </Routes>
   );
 }

2.去除Switch中的<Redirect>,用react-router-dom中的Redirect 替代,或者用 <Navigate> 实现

V5写法:

 <Switch>
   <Redirect from="about" to="about-us" />
 </Switch>

V6写法:

 <Route path="about" render={() => <Navigate to="about-us" />}

3.<Link to>支持相对位置

V5版本的to属性只支持绝对位置,如<Lint to="me">表示<Lint to="/me">,如果当时正在Users组件内,想跳转需要<Lint to="/users/me">。在V6中,Link默认支持相对位置,也就是<Lint to="me"> 在Users组件内会等价于<Lint to="/users/me">,同时支持'..' 和'.'等相对路径写法。

// If your routes look like this
<Route path="app">
<Route path="dashboard">
 <Route path="stats" />
</Route>
</Route>

// and the current URL is /app/dashboard (with or without
// a trailing slash)
<Link to="stats">               => <a href="/app/dashboard/stats">
<Link to="../stats">            => <a href="/app/stats">
<Link to="../../stats">         => <a href="/stats">
<Link to="../../../stats">      => <a href="/stats">

4.使用useNavigate代替useHistory

可以参考上面useNavigate使用,这里不再赘述

总结

V6版本的react-router升级了原有嵌套路由写法,并且重新实现了useNavigate来替代useHistory,整体上更加好理解。当然还有些其他属性和方法没有再介绍,大家如果有其他想知道的也可以回复我来补充。

参考链接:

https://reactrouter.com/docs/en/v6/upgrading/v5
https://reactrouter.com/docs/en/v6/getting-started/overview