React Router6学习(包括组件和Hooks)

React Router6学习

第1节和第2节是课程中的MD笔记, 第3节是在听课过程中自己记的笔记和从网上查的总结, 细节更多.

1. 概述

1.1 React Router以三个不同的包发布到npm上,他们分别是

  1. react-router:路由的核心库, 提供了很多的:组件、钩子。
  2. react-router-dom:包含react-router所有内容,并添加了一些专门用于DOM的组件,例如<BrowserRouter>等。
  3. react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:<NativeRouter>等。

1.2 与React Router 5.x 版本相比,改变了什么?

  1. 内置组件的变化:移除了<Switch>,新增了<Routes/>等。
  2. 语法的变化: component={About} 变为了element={About}等。
  3. 新增多个hook:useParams useNavigate useMatch等。
  4. 官方明确推荐函数式组件了。

2. Component

2.1 BrowserRouter

  1. 说明:<BrowserRouter>用于包裹整个应用。
  2. 示例代码:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from "react-router-dom"
import App from './App';

ReactDOM.render(
  <BrowserRouter>
      <App />
  </BrowserRouter>
  ,document.getElementById('root')
);

2.2 HashRouter

  1. 说明:作用和 <BrowserRouter>一样, 但是<HashRouter>修改的是地址的hash值。
  2. 备注:6.x版本中 <BrowserRouter><HashRouter>的用法与5.x相同。

2.3 <Routes /><Route />

  1. V6版本中移除了先前的Switch,引入了全新的替代者Routes

  2. RoutesRoute要搭配使用,并且必须要用Routes包裹Route

  3. Route相当于一个if语句,如果路径与当前URL匹配,则呈现其对应的组件。

  4. <Route caseSensitive>属性用于指定:匹配时是否区分大小写(默认为false)。

  5. 当URL发生变化时,<Routes>都会查看其所有子 <Route>元素以找到最佳匹配并呈现组件。

  6. <Route>也可以嵌套使用,且可以配合useRoutes()配置“路由表”,但需要通过<Outlet>组件来渲染其子路由。

  7. 示例代码:

<Routes>
    <Route path='/about' element={<About />} />
    <Route path='/home' element={<Home />} />
    <Route path='/' element={<Navigate to='/about'/>}/>
</Routes>
  1. 作用: 修改URL, 且不发送网络请求(路由链接).

  2. 注意: 外侧需要用<BrowserRouter><HashRouter>包裹

  3. 示例代码:

    import {Link} from 'react-router-dom'
    
    function Test() {
    	return (
    		<div>
    			<Link to="/路径">按钮</Link>
    		</div>
    	)
    }
    
  1. 作用: 与 <Link>组件类似, 且可实现导航的"高亮"效果.

2.6 <Navigate>

  1. 作用:只要<Navigate>组件被渲染,就会修改路径,切换视图。
  2. replace属性用于控制跳转模式 (push 或 replace,默认是push)

2.7 <Outlet>

  1. <Route>产生嵌套时,渲染其对应的后续子路由。
  2. 在后续路子由组件的想要展示的位置上使用<Outlet />, 即可渲染。

3. 上课笔记

1. 一级路由

首先需要安装react路由包: npm i react-router-dom

  1. 使用路由:在App.jsx中从react-router-dom中引入BrowserRouter来包裹App组件

  2. 创建路由链接:可以使用react-router-dom中的linkNavLink来创建路由链接,其中NavLink可具有高亮效果。

以前我们的应用如果需要在各个页面之间切换,使用锚点元素实现的话,在每次点击时,页面会被重新加载,React Router提供了 和 来避免这种情况的发生。当你点击时,url会更新,组件会被重新渲染,但是页面不会重新加载。

组件最终会被渲染成HTML标签的, 它的 to、query、hash 属性会被组合在一起并渲染为 href 属性。虽然 Link 被渲染为超链接,但在内部实现上使用脚本拦截了浏览器的默认行为,然后调用了history.pushState 方法。

是的一个特定版本,会在匹配上当前的url的时候给已经渲染的元素添加参数,组件的属性有:

  • activeClassName(string):设置选中样式,默认值为active
  • 需要将className写成一个函数:
  • activeStyle(object):当元素被选中时,为此元素添加样式
  • exact(bool):为true时,只有当导致和完全匹配class和style才会应用
  • strict(bool):为true时,在确定为位置是否与当前URL匹配时,将考虑位置pathname后的斜线
  • isActive(func)判断链接是否激活的额外逻辑的功能
    ————————————————
    版权声明:本文为CSDN博主「冰雪为融」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/lhjuejiang/article/details/80366839
  1. 注册路由:使用的组件由SWitch(已删除)改为Routes,还要需要使用RouteRoute是用来被包裹的,一个<Routes>来包裹多个<Route>, 语法为:

router path 代表的是路由链接地址,React Component代表的是如果匹配成功所使用的组件名字。

<Route path="/router path" element={<React Component/>}>

2. 重定向

当我们什么路由链接都没输入,就相当于是'/'时,就会出现警告,No routes matched location "/", 这个时候我们就需要使用重定向来解决问题。

使用的组件由Redirect改为Navigate,语法为, 其中about为匹配不到路径时所跳转到的组件:

<Route path='/' element={<Navigate to="/about" />} />

Navigate组件只要渲染就会引起视图的切换,可以用此特性来控制当页面达到某一条件时,启用Navigate组件来进行视图切换,举一个小例子

import React, {useState} from 'react'
import { Navigate } from 'react-router-dom'

export default function Home() {
  const [count,setCount] = useState(0)

  return (
    <div>
      <h3>我是Home的内容</h3>
      {
        count === 2 ? <Navigate to='/about'/> : <h4>当前count值是{count}</h4>
      }
      <button onClick={() => setCount(2)}>点我将count变为2</button>
    </div>
  )
}

其实Navigate组件中还有一个属性,replace, 可以控制跳转的模式,默认为false, 即push模式。

<Route path='/' element={<Navigate to="/about" replace/>} />

3. NavLink高亮

NavLink路由链接在点击的时候,会给他加一个class属性,值默认是active,在使用bootstrap的时候就会出现高亮效果。

首先我们要知道在我们点击一个连接的时候,会传入一个对象,里面有isActive属性,值为true。

利用这个特性我们可以写一个函数来计算样式,然后在className中使用这个函数,关键代码如下:

function computedClassName({isActive}) {
    return isActive? 'list-group-item atguigu': 'list-group-item'
}

<div className="list-group">
    {/* 路由链接 */}
    <NavLink className={computedClassName} to="/about">About</NavLink>
    <NavLink className={computedClassName} to="/home">Home</NavLink>
</div>

4. useRoutes重定向

之前我们是这样注册路由的:

<Routes>
    <Route path='/about' element={<About />} />
    <Route path='/home' element={<Home />} />
    <Route path='/' element={<Navigate to='/about'/>}/>
</Routes>

其实可以发现中间除了path值和element值不一样以外,基本都是相同的结构,所以我们就可以使用useRoutes来注册路由。

在我们有路由的项目中,一般会有两个特殊的文件夹,一个是pages,里面存放的是各个路由的组件。另外一个是routes,里面存放的是路由表,里面的index.js一般是如下类似代码:

import About from '../pages/About'
import Home from '../pages/Home'
import { Navigate } from 'react-router-dom'

// 创建路由表,可以用来注册路由
const routerList =  [
    {
        path:'/about',
        element:<About />
    },
    {
        path:'/home',
        element:<Home />
    },
    {
        path:'/',
        element:<Navigate to='/about'/>
    },
]

export default routerList;

然后我们只需要在需要使用注册路由的组件里使用useRoutesHooks来实现了:

import {NavLink,useRoutes} from "react-router-dom"

export default function App() {
  // 根据路由表生成对应的路由
  const element = useRoutes(routerList)
  
  ......
  {/* 路由链接 */}
  <NavLink className='list-group-item' to="/about">About</NavLink>
  <NavLink className='list-group-item' to="/home">Home</NavLink>
  {/* 注册路由 */}
  {element}
  ......
}

这样做的好处是可以把整个应用所有的路由都可以在routes文件夹下进行统一的管理,而且能够让代码可读性更好,也契合了未来Hooks发展的方向。

5. 嵌套路由

outlet

Outlet像是一个槽位,如果匹配上了就在Outlet组件所在的地方进行组件的展示,也就是说Outlet可以指定组件呈现的位置。

to

to属性内可以直接写最终地址,比如之前是/home/news, 现在可以直接写news,注意不要写成/news, 否则就成了从一级路由news里找,即从根路径下开始找。

routes/index.js

import About from '../pages/About'
import Home from '../pages/Home'
import { Navigate } from 'react-router-dom'
import Message from '../pages/Message'
import News from '../pages/News'


// 创建路由表,可以用来注册路由
const routerList =  [
    {
        path:'/about',
        element:<About />
    },
    {
        path:'/home',
        element:<Home />,
        children:[
            {
                path:'news',
                element:<News/>
            },
            {
                path:'message',
                element:<Message/>
            }
        ]
    },
    {
        path:'/',
        element:<Navigate to='/about'/>
    }
]

export default routerList;

Home.jsx关键代码:

import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'

export default function Home() {

	return (
        <div>
          <h3>我是Home的内容</h3>
          <div>
            <ul className='nav nav-tabs'>
              <li>
                <NavLink className='list-group-item' to='news'>News</NavLink>
              </li>
              <li>
                <NavLink className='list-group-item' to='message'>Message</NavLink>
              </li>
            </ul>
            {/* 指定路由组件呈现的位置 */}
            <Outlet />
          </div>
        </div>
  )
}

一共有三种路由路径的书写方式,以二级路由home组件下的news为例:

  • to='/home/news'
  • to='news'
  • to='./news'

end

还有一个值得一提的事是,目前我们页面所呈现的高亮的地方不仅有news, 而且还有一级路由home也是高亮的,我们如果不想让他高亮,那么就可以在该组件上加一个属性end,来代表当匹配的是该路由的子路由时,该路由不高亮。

<NavLink className='list-group-item' to="/home" end>Home</NavLink>

6. 路由的params参数以及useMatch钩子

一共有三种方式可以在路由进行传参操作:1. params参数 2. search 3. location.state

我们首先讲第一种,使用params参数:

// 1.Link路由链接的书写方式
<Link to={`detail/${message.id}/${message.title}/${message.content}`}>{message.title}</Link>
// 2.路由表匹配规则
{
    path:'detail/:id/:title/:content',
    element:<Detail />
}

// 接收使用
import { useParams } from "react-router-dom"

export default function Detail() {
    // 接收得到的参数是一个对象,我们可以使用结构的方式来获得他
    const { id, title, content } = useParams()
    return (
        <ul>
            <li>消息编号:{id}</li>
            <li>消息标题:{title}</li>
            <li>消息内容:{content}</li>
        </ul>
    )
}

useMatch

image-20220304205613745

我们还可以使用useMatch钩子来获得之前match里面的参数,比如patten里的path参数等,具体使用语法如下,我们需要在所需要使用match参数的组件里把当前路径包括params写好:

import {useMatch} from "react-router-dom"
......
	const match = useMatch('/home/message/detail/:id/:title/:content')
    console.log(match)

7. 路由的search参数以及useLocation钩子

一共有三种方式可以在路由进行传参操作:1. params参数 2. search 3. location.state

我们来介绍第二种, 使用serach:

// 1. search传参的书写方式
messages.map(message => {
                        return (
                            // 路由链接
                            <li key={message.id}>
                                {/* search传参 */}
                                <Link to={`detail?id=${message.id}&title=${message.title}&content=${message.content}`}>{message.title}</Link>
                            </li>
                        )
	})
// 2.routes路由表不用改变任何东西

// 3.在接收参数的组件里,要使用useSearchParams hook来接收参数
import React from 'react'
import { useSearchParams } from 'react-router-dom'
export default function Detail() {
	// 类似于useState的使用方式,所存放的数据在search.get('...')里
    const [search, setSearch] = useSearchParams()
    return (
        <ul>
            <li>消息编号:{search.get('id')}</li>
            <li>消息标题:{search.get('title')}</li>
            <li>消息内容:{search.get('content')}</li>
            <li><button onClick={() => setSearch('id=008&title=哈哈&content=嘻嘻')}>点我更新一下search参数</button></li>
        </ul>
    )
}

useLocation

image-20220304215254053

另外我们还可以使用useLocation钩子来获得loaction的数据,里面包括了pathname和经过转化后的search,具体的语法是:

import { useLocation } from 'react-router-dom'
......
	const location = useLocation()
    console.log(location);

8. 路由的state参数

一共有三种方式可以在路由进行传参操作:1. params参数 2. search 3. location.state

我们来介绍第三种, 使用location.state,当然就是使用的上面提到过的useLocation钩子:

// 1. 路由链接
    <li key={message.id}>
        {/* state传参 */}
        <Link 
            to='detail' 
            state={{
                id:message.id,
                    title:message.title,
                        content:message.content
            }}
            >{message.title}</Link>
    </li>
// 2. routes路由表不需要做任何的改动,同search一样

// 3. 在接收参数的组件里,要使用useLocation hook来接收参数
import React from 'react'
import { useLocation } from 'react-router-dom'

export default function Detail() {
    const location = useLocation()
    const { id, title, content } = location.state
    return (
        <ul>
            <li>消息编号:{id}</li>
            <li>消息标题:{title}</li>
            <li>消息内容:{content}</li>
        </ul>
    )
}

9. 编程式路由导航

之前我们都是借助this.props.history来对对象上的API对操作路由跳转\前进和后退, 可是在函数式组件中我们并不能使用this, 这个时候我们借助useNavigate这个Hook来实现.

首先我们来对其中的replace和state传参来进行说明:

// 引入useNavigate钩子
import { Link, Outlet,useNavigate } from 'react-router-dom'

// 使用钩子,得到的navigate是一个函数
const navigate = useNavigate()
const showDetail = (message) => {
    const {id, title, content} = message
    navigate('detail',{
        // 指定是否为替换的方式
        replace:false,
        // 传递state参数
        state:{
            id,
            title,
            content
        }
    })
}

// 在组件上使用函数来使用该navigate函数
<button onClick={() => showDetail(message)}>详细信息</button>

但是我们知道在之前的react router5里面非路由组件是不能使用this.props.history的, 但是在react router6里面, 我们只需要使用useNavigate这个钩子, 就可以实现非路由组件使用编程式路由导航, 并且可以实现页面的回退和前进, 具体实现如下所示, components文件夹(此文件夹存放非路由组件)下的Header组件源代码如下所示:

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

export default function Header() {
    const navigate = useNavigate()
    
    const backward = () => {
        navigate(-1)
    }

    const forward = () => {
        navigate(1)
    }
    return (
        <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header">
                <h2>React Router6 Demo</h2>
                <button onClick={backward}>⬅后退</button>
                <button onClick={forward}>➡前进</button>
            </div>
        </div>
    )
}

10. useInRouterContext Hook的使用

作用: 如果组件在<Router>的上下文中呈现, 则useInRouterContext钩子返回为 true, 否则返回 false.

比如说我们的App组件是被包裹的, App组件及其所有的子组件都是处于路由的上下文环境中的, 不区分是否是路由组件.

11. useNavigationType() Hook的使用

  1. 作用:返回当前的导航类型(用户是如何来到当前页面的)
  2. 返回值: POP PUSH REPLACE
  3. 备注: POP是指在浏览器中直接打开了这个路由组件(刷新页面)

12. useOutlet()

  1. 作用, 用来呈现当前组件中要渲染的嵌套路由
  2. 示例代码:
const result = useOutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象

13. useResolvedPath()

  1. 作用: 给定一个url值, 解析其中的: path search 和 hash值.
posted @ 2022-03-05 16:07  bleaka  阅读(1856)  评论(0编辑  收藏  举报