react-router-dom总结

383 阅读4分钟

路由

概念

路由是根据浏览器不同的url地址展示不同的页面或内容的一种方案,react-router-dom是一个针对react而设计的路由解决方案,可以友好的解决react component到url之间的同步问题,本文将主要针对react-router-dom v6进行整合

安装

npm install react-router-dom@6

package

react-routernpm上发布了三个包:

  • react-router :包括了react-router大部分核心功能,包括路由匹配算法和一些核心组件
  • react-router-dom :包括了react-router所有的内容,并且添加了一些核心组件如<BrowerRouter/><HashRouter/>
  • react-router-native:包括了react native所有内容,并添加了一些额外的native特定api,不作讨论

<BrowerRouter/><HashRouter/>

特性<BrowerRouter/>
原理historyhash(#符号)后的值
交互后台接收完整请求后台只接收#前内容,#后由浏览器解析
传值loaction.key`loaction.state`不向后台传值

路由的使用方法

<BrowserRouter>

使用h5内置history内部堆栈进行导航

import * as React from "react";
import * as ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter>
    {/* The rest of your app goes here */}
  </BrowserRouter>,
  root
);

<HashRouter>

可以将当前位置暂存到location.hash中,但是不会发送到服务器

import * as React from "react";
import * as ReactDOM from "react-dom";
import { HashRouter } from "react-router-dom";

ReactDOM.render(
  <HashRouter>
    {/* The rest of your app goes here */}
  </HashRouter>,
  root
);

<MemoryRouter>

在某些情况下比如移动端,没有url的情况,只能使用Memory记住,用于非dom环境

import * as React from "react";
import { create } from "react-test-renderer";
import {
  MemoryRouter,
  Routes,
  Route,
} from "react-router-dom";

describe("My app", () => {
  it("renders correctly", () => {
    let renderer = create(
      <MemoryRouter initialEntries={["/users/mjackson"]}>
        <Routes>
          <Route path="users" element={<Users />}>
            <Route path=":id" element={<UserProfile />} />
          </Route>
        </Routes>
      </MemoryRouter>
    );

    expect(renderer.toJSON()).toMatchSnapshot();
  });
});

<Link>

使用<Link>标签可以让用户通过点击导航到另一个页面,内部是一个<a>标签

import * as React from "react";
import { Link } from "react-router-dom";

function UsersIndexPage({ users }) {
  return (
    <div>
      <h1>Users</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>
            <Link to={user.id}>{user.name}</Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

<NavLink>

<Link/>相比唯一区别在于<NavLink>知道其是否被激活,在其激活后会将activeClassName自动添加到标签上

import * as React from "react";
import { NavLink } from "react-router-dom";

function NavList() {
  // This styling will be applied to a <NavLink> when the
  // route that it links to is currently selected.
  let activeStyle = {
    textDecoration: "underline"
  };

  let activeClassName = "underline"

  return (
    <nav>
      <ul>
        <li>
          <NavLink
            to="messages"
            style={({ isActive }) =>
              isActive ? activeStyle : undefined
            }
          >
            Messages
          </NavLink>
        </li>
        <li>
          <NavLink
            to="tasks"
            className={({ isActive }) =>
              isActive ? activeClassName : undefined
            }
          >
            Tasks
          </NavLink>
        </li>
        <li>
          <NavLink
            to="tasks"
          >
            {({ isActive }) => (
              <span className={isActive ? activeClassName : undefined}>
                Tasks
              </span>
            ))}
          </NavLink>
        </li>
      </ul>
    </nav>
  );
}

<Navigate>

v6中新的重定向组件

import * as React from "react";
import { Navigate } from "react-router-dom";

class LoginForm extends React.Component {
  state = { user: null, error: null };

  async handleSubmit(event) {
    event.preventDefault();
    try {
      let user = await login(event.target);
      this.setState({ user });
    } catch (error) {
      this.setState({ error });
    }
  }

  render() {
    let { user, error } = this.state;
    return (
      <div>
        {error && <p>{error.message}</p>}
        {user && (
          <Navigate to="/dashboard" replace={true} />
          {/*当设置为 true 时,点击链接后将替换历史堆栈中的当前条目,而不是添加新条目。默认为 false。*/}
        )}
        <form
          onSubmit={(event) => this.handleSubmit(event)}
        >
          <input type="text" name="username" />
          <input type="password" name="password" />
        </form>
      </div>
    );
  }
}

<Outlet>

新的this.props.children,在父组件中使用从而展示出其子路由,并且允许在渲染子路由时嵌套UI

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      {/* This element will render either <DashboardMessages> when the URL is
          "/messages", <DashboardTasks> at "/tasks", or null if it is "/"
      */}
      <Outlet />
    </div>
  );
}

function App() {
  return (
    <Routes>
      <Route path="/" element={<Dashboard />}>
        <Route
          path="messages"
          element={<DashboardMessages />}
        />
        <Route path="tasks" element={<DashboardTasks />} />
      </Route>
    </Routes>
  );
}

<Routes><Route>

v6版本中<Switch/>被重新更名为Routers,但是大部分功能不变,并且现在一个页面允许多个Routes

<Routes>
  <Route path="/" element={<Dashboard />}>
    <Route
      path="messages"
      element={<DashboardMessages />}
    />
    <Route path="tasks" element={<DashboardTasks />} />
  </Route>
  <Route path="about" element={<AboutPage />} />
</Routes>

route中component和render被element替代

useHref

useHref钩子返回一个 URL,该 URL 可用于链接到给定to位置,甚至在 React Router 之外。

useLocation

返回当前location对象。

import * as React from 'react';
import { useLocation } from 'react-router-dom';

function App() {
  let location = useLocation();

  React.useEffect(() => {
    ga('send', 'pageview');
  }, [location]);

  return (
    // ...
  );
}

useNavigate

返回一个函数,允许您以useNavigate编程方式导航,例如在提交表单之后。

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

function SignupForm() {
  let navigate = useNavigate();

  async function handleSubmit(event) {
    event.preventDefault();
    await submitForm(event.target);
    navigate("../success", { replace: true });
  }

  return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

实际上是对v5useHistory的改动,navigate()替代了history.push(),并且支持传递增量,如:

前进:navigate(1)

后退:navigate(-1)

useRoutes

等同于<Routes/>

import * as React from "react";
import { useRoutes } from "react-router-dom";

function App() {
  let element = useRoutes([
    {
      path: "/",
      element: <Dashboard />,
      children: [
        {
          path: "messages",
          element: <DashboardMessages />,
        },
        { path: "tasks", element: <DashboardTasks /> },
      ],
    },
    { path: "team", element: <AboutPage /> },
  ]);

  return element;
}

路由跳转方式

声明式

<NavLink to="/films" activeClassName="active">films</NavLink>
<NavLink to="/cinemas" activeClassName="active">cinemas</NavLink>
<NavLink to="/center" activeClassName="active">center</NavLink>

编程式导航

//v5
this.props.history.push(`/center`)
useHistory()

//v6
useNavigate();

路由传参

v5

方法名link传递参数获取优点缺点
params<link to="/path/2">xxx</Link>this.props.history.push({pathname:"/path/" + name});this.props.match.params.name地址栏刷新不丢失只能传字符串
query<Link to={{ path : ' /query' , query : { name : 'sunny' }}}>this.props.history.push({pathname:"/query",query: { name : 'sunny' }});this.props.location.query.name传参不显示到地址栏刷新参数丢失
state<Link to={{ path : ' /sort ' , state : { name : 'sunny' }}}>this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});this.props.location.query.state同query同query
search<link to="/path/2">xxx</Link>this.props.history.push({pathname:"/web/departManange?tenantId" + row.tenantId});this.props.location.search同params同params

v6

子路由形式(对应params)

// 路由文件
<Route path='/list/:id' element={<List />}></Route>

// App.jsx中
<li><Link to="/list/123">列表页</Link></li>

获取

import React from 'react'
import {useParams} from 'react-router-dom'

export default function List() {
  const params = useParams()
  console.log(params)   // {id: '123'}
  return <h2>List列表页</h2>
}

问号形式

地址栏携带参数还可以通过问号形式:

<li><Link to="/detail?id=456">详情页</Link></li>

获取该参数的方式,需要使用useSearchParams这个路由hook,并且调用getAll方法才能获取得到:

import React from 'react'
import {useSearchParams} from 'react-router-dom'

export default function Detail() {
  const [params] = useSearchParams()
  console.log(params.getAll('id'))  // ['456']
  return <h2>Detail详情页</h2>
}

state携带

以上两种携带参数的方式,都只能携带简单的参数,如果数据量较大,其实并不方便,因此,在使用useNavigate()这个hook实现跳转时,其实也可以携带参数:

navigate('/home', {
  state: {id: 789}
})

获取该参数的方式便是使用useLocation这个路由hook:

import React from 'react'
import { useLocation } from 'react-router-dom'

export default function Home() {
  const location = useLocation()
  console.log(location.state.id)  // 789
  return <h2>Home首页</h2>
}

404匹配

如果碰上未在路由文件中有做配置的路由,便可认定为404页面:

// 路由组件
import Error from '../pages/Error'


<Route path='/' element={<App />}>
  ...省略部分路由内容
  <Route path='*' element={<Error />}></Route>
</Route>

此时只要是非配置过的页面,便是404页面。