1. 程式人生 > 其它 >React Router6學習(包括元件和Hooks)

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

我們還可以使用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

另外我們還可以使用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值.