从Vue2.0到React17——React路由入门(二)

2,282 阅读6分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

前言

React作为一个MVVM框架,路由功能是必不可少的,回顾我们在使用Vue Router的过程中,最常用的一些功能是路由页面的渲染路由页面内容的添加路由地址的配置路由跳转路由传参嵌套路由等等,本文将去React Router中寻找这些常用功能的是如何实现的,来带你入门 React Router5.0

在上篇文章中介绍了路由页面如何渲染 、路由页面内容如何设置、如何设置路由地址,本文将继续介绍路由如何跳转、如何接收路由的传参、路由懒加载。

一、路由如何跳转

在Vue Router中有两种方式进行路由跳转,一种是声明式,一种是编程式。

声明式编程式
<router-link :to="...">router.push(...)

那React Router中是否也有声明式和编程式的路由跳转?举一个例子来介绍,例如:实现一个点击第一个页面跳转到第二个页面,点击第二个页面跳转到第一个页面。

1.1 声明式路由跳转

使用 Link 组件来实现跳转,其属性to可设置要跳转的路由地址,切记 Link 组件必须在 Route 组件内使用

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route ,Link} from "react-router-dom";
ReactDOM.render(
  <div>
    <BrowserRouter>
      <Route
        path="/one"
        render={() => {
          return (
            <div><Link to="/two">去第二个页面</Link></div>
          )
        }}
      >
      </Route>
      <Route
        path="/two"
        render={() => {
          return (
            <div><Link to="/one">去第一个页面</Link></div>
          )
        }}
      ></Route>
    </BrowserRouter>
  </div>,
  document.getElementById('root')
);

1.2 编程式路由跳转

在类组件和函数组件中的用法是不同的,分别来介绍。

1.2.1 函数组件中的编程式路由跳转

执行useHistory() Hook 来获取history,然后调用history.push(path)进行路由跳转。

1.2.2 类组件中的编程式路由跳转

在类组件中通过props获取到history,然后调用history.push(path)进行路由跳转。

具体实现看下面demo。

import React from 'react';
import ReactDOM from 'react-dom';
import {
  BrowserRouter,
  Route,
  useHistory
} from "react-router-dom";
class OnePage extends React.Component {
  constructor(props) {
    super(props)
    const { history } = this.props;
    this.history = history;
    this.jump = this.jump.bind(this)
  }
  jump = () => {
    this.history.push('/two')
  }
  render() {
    return (
      <div>
        <div onClick={this.jump}>去第二个页面</div>
      </div>
    )
  }
}
const TwoPage = () => {
  const history = useHistory();
  const handleClick = () => {
    history.push("/one");
  }
  return (
    <div onClick={handleClick}>去第一个页面</div>
  )
}
ReactDOM.render(
  <div>
    <BrowserRouter>
      <Route
        path="/one"
        component={OnePage}
      >
      </Route>
      <Route
        path="/two"
        component={TwoPage}
      ></Route>
    </BrowserRouter>
  </div>,
  document.getElementById('root')
);

二、如何接收路由的传参

在类组件和函数组件中的接收路由参数的做法是不同的,要分别来介绍。另外每一种接收路由的传参都对应着一种传递路由参数的方法,且在类组件和函数组件中是一致的,会穿插在每种接收路由的传参的方法中介绍。

2.1 在函数组件中接收路由传参

在函数组件中有三个Hook(useLocationuseParamsuseRouteMatch)可以接收路由参数,下面来详细介绍各自的使用方法。

2.1.1 useLocation

const location = useLocation();
console.log(location)

先在函数组件中执行useLocation,并将结果打印出来,看一下结果,如下图所示:

image.png

主要关注结果中的searchstate这个两个属性,这两个属性是用来接收路由的传参。

那要怎么传参,才能用useLocation返回的对象中的searchstate接收到路由的传参。在上一小节可知路由的跳转有两种,一种是声明式,一种是编程式。

search接收跳转地址的请求参数,如:/one?id=1时,search?id=1,可以在声明式跳转中这样传参。

state接收history.push中的第二参数作为传参,如:history.push('/one',{name:'第一个页面'}),一般在编程式跳转中这样传参。

import React from 'react';
import ReactDOM from 'react-dom';
import {
  BrowserRouter, 
  Route,
  Link,
  useLocation,
  useHistory
} from "react-router-dom";
const OnePage = () =>{
  const location = useLocation();
  console.log('第一个页面',location)
  return (
    <div><Link to="/two?id=2">去第二个页面</Link></div>
  )
}
const TwoPage= () =>{
  const location = useLocation();
  console.log('第二个页面',location)
  const history = useHistory();
  const toOnePage = () =>{
    history.push('/one',{name:'第一个页面'})
  }
  return (
    <div onClick={toOnePage}>去第一个页面</div>
  )
}
ReactDOM.render(
  <div>
    <BrowserRouter>
      <Route
        path="/one"
        component={OnePage}
      >
      </Route>
      <Route
        path="/two"
        component={TwoPage}
      ></Route>
    </BrowserRouter>
  </div>,
  document.getElementById('root')
);

将 OnePage 和 TwoPage 两个组件中的执行useLocation()的结果都打印出来,如下图所示:

image.png

当使用history.push('/one',{name:'第一个页面'})跳转到第一个页面时,useLocation()的结果中的state的值为{name: "第一个页面"}

当使用<Link to="/two?id=2">去第二个页面</Link>跳转到第二个页面时,useLocation()的结果中的search的值为"?id=2"

2.1.2 useParams

useParams接收跳转地址的路径参数,如/one/1,其中1就是路径参数。

要先用特定的写法给 Route 组件的path属性添加路径,才能用useParams接收到路径参数。

例如path设置为/one/:id/:num,若路由跳转地址为/one/2/3/4,那么用useParams接收的值为{id:2,num:3}

下面用一个例子具体演示一下,还是点击第一个页面跳转第二页面,点击第二个页面跳转第一页面,注意观察各自 Route 组件的path属性值、路由跳转地址和各自组件内执行useParams()的结果。

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter,
  Route,
  Link,
  useHistory,
  useParams
} from "react-router-dom";
const OnePage = () =>{
  const params = useParams();
  console.log('第一个页面',params);
  return (
    <div><Link to="/two/2/3">去第二个页面</Link></div>
  )
}
const TwoPage= () =>{
  const params = useParams();
  console.log('第二个页面',params);
  const history = useHistory();
  const toOnePage = () =>{
    history.push('/one/1/3')
  }
  return (
    <div onClick={toOnePage}>去第一个页面</div>
  )
}
ReactDOM.render(
  <div>
    <BrowserRouter>
      <Route
        path="/one/:id/:num"
        component={OnePage}
      >
    </Route>
      <Route
        path="/two/:id"
        component={TwoPage}
      ></Route>
    </BrowserRouter>
  </div>,
  document.getElementById('root')
);

将上面demo中各自组件内执行useParams()的结果打印出来,如下图所示:

image.png

此外要特别注意,若给一个 Route 组件设置的path带路径参数的变量,设置几个,访问这个 Route 组件的URL也要有几个路径参数,否是访问不了。

2.1.3 useRouteMatch

执行useRouteMatch()可得到当前路由页面的路由数据。数据中有个params的字段,其值和执行useParams()得到数据一样。

所以使用useRouteMatch获取路由参数,也要先用特定的写法给 Route 组件的path属性添加路径,才能用useRouteMatch接收到路径参数。

例如将渲染“第一页面”的 Route 组件的path设置为/one/:id/:num,若路由跳转地址为/one/2/3/4,那么在“第一页面”路由页面组件中执行useRouteMatch()

import {
  useRouteMatch
} from "react-router-dom";
const OnePage = () =>{
  const match = useRouteMatch();
  console.log(match);
  return (
    <div>第一个页面</div>
  )
}

可得到当前路由页面的路由数据match如下所示:

image.png

其中红框部分就是路由参数,是不是和执行useParams()得到数据一样?

2.2 在类组件中接收路由传参

在类组件中通过props获取到locationmatch,然后在其中获取到路由参数search(请求参数)、params(路径参数)和state

在“第一页面”这个路由页面组件中把props打印出来。

class OnePage extends React.Component {
  constructor(props) {
    super(props)
    console.log(this.props)
  }
  render() {
    return (
      <div>
        <div>第一个页面</div>
      </div>
    )
  }
}

打印结果如下图所示:

image.png

至于路由参数search(请求参数)、params(路径参数)和state如何传递已经在上小节【在函数组件中接收路由传参】中介绍过了。

三、路由懒加载

使用React.lazy定义一个动态加载的组件,来实现路由懒加载。

注意:React.lazy定义的组件必须被包裹在<React.Suspense></React.Suspense>中使用,且React.Suspense的属性fallback为必填属性。

例如把OnePage这个路由页面组件进行懒加载,实现代码如下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import {
  BrowserRouter,
  Route,
} from "react-router-dom";
const OnePage = React.lazy(() => import('./OnePage'));
const Spinner = () => {
  return (
    <div>加载中……</div>
  )
}
ReactDOM.render(
  <div>
    <React.Suspense fallback={<Spinner/>}>
      <BrowserRouter>
        <Route
          path="/"
          component={OnePage}
        >
        </Route>
      </BrowserRouter>
    </React.Suspense>
  </div>,
  document.getElementById('root')
);