??React全家桶??
React全家桶 | 地址 |
---|---|
React全家桶(一)之React入門?? | https://blog.csdn.net/article/124120080 |
React全家桶(二)之組件化編程?? | https://blog.csdn.net/article/124194107 |
React全家桶(三)之React腳手架?? | https://blog.csdn.net/article/124281588 |
React全家桶(四)之axios請求數據?? | https://blog.csdn.net/article/124281650 |
React全家桶(五)之React-router路由?? | https://blog.csdn.net/article/124453733 |
React全家桶(六)之redux狀態管理?? | https://blog.csdn.net/article/124454379 |
React 路由
?目錄總覽:
[圖片上傳失敗...(image-8f5282-1651062725158)]
路由基本概念
現代的前端應用大多都是SPA(單頁應用程序),也就是只有一個HTML頁面的應用程序。因為它的用戶體驗更好、對服務器的壓力更小,所以更受歡迎。為了有效的使用單個頁面來管理原來多個頁面的功能,前端路由應運而生。
SPA的概念
1.單頁Web應用(single page web application,SPA)。
2.整個應用只有一個完整的html頁面。
3.點擊頁面中的鏈接不會刷新頁面,只會做頁面的局部更新。
4.數據都需要通過ajax請求獲取, 并在前端異步展現。單頁面+多組件
路由的基本概念與原理
路由概念
- 路由是一個比較廣義和抽象的概念,路由的本質就是對應關系。
- 一個路由就是一個映射關系,一個key對應一個value
- key為路徑, value可能是function或component
路由的分類:
- 后端路由:(根據路徑向服務器端請求對應數據)
1)理解: value是function, 用來處理客戶端提交的請求。
2)注冊路由: router.get(path, function(req, res))
3)工作過程:當node接收到一個請求時, 根據請求路徑找到匹配的路由, 調用路由中的函數來處理請求, 返回響應數據- 前端路由:(根據路徑展示對應的組件)
1)瀏覽器端路由,value是component,用于展示頁面內容。
2)注冊路由:<Route path="/test" component={Test}>
3)工作過程:當瀏覽器的path變為/test時, 當前路由組件就會變為Test組件
react-router-dom
- react的一個插件庫。
- 專門用來實現一個SPA應用。
- 基于react的項目基本都會用到此庫。
下載方式:install i react-router-dom`
路由的基本使用
一、路由組件(Route,Link)
1. 使用步驟
- 安裝: npm add react-router-dom
- 導入路由的三個核心組件:BrowserRouter/Route/Link
- 使用Router組件包裹整個應用
- 使用Link組件作為導航菜單(路由入口)
- 使用Route組件配置路由規則和要展示的組件(路由出口)
[圖片上傳失敗...(image-2d7173-1651062725158)]
2. 路由組件說明
- Router組件:包裹整個應用,一個React應用只需要使用一次
- 兩種常見Router:HashRouter、BrowserRouter
- HashRouter: 使用URL的哈希值實現(localhost:3000/#/first)
- 推薦使用BrowserRouter:使用H5的history api實現(localhost:3000/first)
- Link組件:用于指定導航鏈接(a標簽)
- to屬性:瀏覽器地址欄中的pathname(location.pathname)
- Route組件:指定路由展示組件相關信息
- path屬性:路由規則
- component屬性:展示的組件
- Route組件寫在哪,渲染出來的組件就展示在哪
3. 路由的執行過程
最后,我們再對React路由實現的原理做一個小結:
(1)使用Link等標簽實現對瀏覽器path的操作(本質上是對BOM對象的history進行操作)
(2)當前端路由器檢測到瀏覽器的path發生了變化
(3)前端路由內部遍歷所有Route組件,使用路由規則(path)與pathname進行匹配
(4)當路由規則(path)能匹配地址欄中的pathname時,就展示該Route組件的內容
[圖片上傳失敗...(image-5f0857-1651062725158)]
路由組件與一般組件的不同之處:
1.寫法不同:
一般組件:<Demo/>
路由組件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
一般組件: components
路由組件: pages
3.接收到的props不同:
一般組件: 寫組件標簽時傳遞了什么,props就能收到什么
路由組件: 接收帶三個固定的屬性
history:
go: ? go(n)
goBack: ? goBack()
goForward: ? goForward()
push: ? push(path, state)
replace: ? replace(path, state)
location:
pathname: "/home"
search: ""
state: undefined
match:
params: {}
path: "/home"
url: "/home"
路由組件與一般組件props接收的數據對比圖:
[圖片上傳失敗...(image-541ad4-1651062725158)]
二、React-route庫的內置組件
1. NavLink組件
- NavLink組件是在Link組件的基礎上做了高亮特效的增強,
activeClassName="active"
[圖片上傳失敗...(image-553928-1651062725158)]
拓展:封裝MyNavLink
[圖片上傳失敗...(image-c0b9a3-1651062725158)]
NavLink總結:
NavLink與封裝MyNavLink
- NavLink可以實現路由鏈接的高亮,通過activeclassName指定樣式名
- 標簽體內容是一個特殊的標簽屬性
- 通過this.props.children可以獲取標簽體內容
2. Switch的使用(單一匹配)
當路由中出現了2個或者2個以上的path同時匹配的情況,那么實際上對應的路由組件都會被渲染。如果我們想要說只渲染(掛載)第一個匹配上的組件的話,那么我們可以使用<Switch>組件來解決。
- 未使用Switch組件的情況:
/home
對應著2個組件
[圖片上傳失敗...(image-1f43f7-1651062725158)]
[圖片上傳失敗...(image-1b137-1651062725158)]
- 引入Switch組件的情況
[圖片上傳失敗...(image-8b6d27-1651062725158)]
[圖片上傳失敗...(image-6b28e3-1651062725158)]
Switch總結:
Switch的使用
1.通常情況下,path和component是一一對應的關系。 2.Switch可以提高路由匹配效率(單一匹配)。
3. Redirect (路由重定向)
我們在進入網站的時候,網站的導航欄中往往會有一個默認選中的標簽,對于這種場景,React路由其實也為我們提供了對應的組件來幫助我們簡化開發,那就是Redirect組件。我們在快速入門的案例中新增加一個功能,進入首頁后,默認跳轉到/about
路徑下。
[圖片上傳失敗...(image-dd0f42-1651062725158)]
[圖片上傳失敗...(image-3e5d75-1651062725158)]
Redirect總結:
Redirect的使用
1.一般寫在所有路由注冊的最下方,當所有路由都無法匹配時,跳轉到Redirect指定的路由 2.具體編碼:
<Switch> <Route path="/about" component={About}/> <Route path="/home" component={Home}/> <Redirect to="/about"/> </Switch>
三、解決樣式丟失問題
樣式丟失:
- BrowserRouter中存在的問題:相對路徑導致,多級路徑刷新頁面樣式丟失。
- 樣式丟失的原因:混入了路徑,一刷新,就找不到對應的樣式css文件了
解決方法:
1、改變index.html中引入bootstap的路徑
原本:
<link rel="stylesheet" href="./css/bootstrap.css">
刪去點:
<link rel="stylesheet" href="/css/bootstrap.css">
原因:因為加了點的話,就是相對路徑,就是從當前文件出發來查找,所以就從localhost:3000/gogocj文件路徑出發來找就錯了
如果不寫點的話,就是直接在 localhost:3000/ 中找
2、修改引入link路徑( %PUBLIC_URL% 這是適用于在React中寫,指的就是public目錄,相當于寫死了)
<link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">
這種就是絕對路徑了,更加不可能有問題了,因為 %PUBLIC_URL% 表示的就是 localhost:3000
3、不改變link的路徑,
<link rel="stylesheet" href="./css/bootstrap.css">
把BrowserRouter改成HashRouter原因:hash路由中會有一個#,#號后面的東西都代表是前端的東西,是不會像后端請求的
然后一刷新的時候,都是直接忽略#號后面的東西了
也就是:我們在 localhost:3000/#/gogocj/home 刷新頁面的時候,會把#省略,直接給localhost:3000/發送請求
總結:
解決多級路徑刷新頁面樣式丟失的問題:
1.public/index.html 中 引入樣式時不寫 ./ 寫 / (常用) 2.public/index.html 中 引入樣式時不寫 ./ 寫 %PUBLIC_URL% (常用) 3.使用HashRouter
總結:前兩種都是改變link的路徑,第三種是直接修改了路由的模式。
四、路由的模糊匹配和嚴格匹配
我們一般使用react路由時,鏈接要與組件相匹配,如下使用
<NavLink to='/home'>Home</NavLink>
<Route path='/home' component={Home}/>
1. 模糊匹配
如下所示,當鏈接第一級路徑能匹配上相應的組件,而鏈接后面再多幾級路徑也不會產生影響,依舊能正常匹配(當然鏈接路徑少于組件路徑時不能匹配,像to='/a/home/b'這樣也無法匹配)
<NavLink to='/home/a/b'>Home</NavLink>
<Route path='/home' component={Home}/> //可以匹配
<Route path='/home/a/b/c' component={Home}/> //不可以匹配
<Route path='/a/home/b' component={Home}/> //不可以匹配
2. 嚴格匹配
有時我們不希望它進行模糊匹配,那么需要在Route上加一個屬性exact,即嚴格的。這樣模糊匹配將無法再正常匹配
<NavLink to='/home/a/b'>Home</NavLink>
<Route exact path='/home' component={Home}/>//不可以匹配
<Route exact path='/home/a/b' component={Home}/>//可以匹配 鏈接路徑和組件路徑,必須一模一樣
總結:
路由的嚴格匹配與模糊匹配
1.默認使用的是模糊匹配(簡單記:【輸入的路徑】必須包含要【匹配的路徑】,且順序要一致) 2.開啟嚴格匹配:`<Route exact={true} path="/about" component={About}/>` 3.嚴格匹配不要隨便開啟,需要再開,有些時候開啟會導致無法繼續匹配二級路由
五、嵌套路由
1. 嵌套子路由
假如我們要給Home組件注冊兩個子組件路由的時候,
文件建立方式:
因為我們做的News和Message組件其實都是Home組件的子組件的,所以有兩種文件建立的模式
(1)在Home文件夾下直接再建立文件夾
(2)在和Home同目錄下,建立名為 Home_news的文件夾
路由的匹配
-
路由是有先后的注冊順序的,會先從最先注冊的路由開始查詢的
所以我們子組件嵌套路由,中的 to屬性要做前面添加父組件的路徑,比如:
<MyNavLink to="/home/news">News</MyNavLink>
//原因:由于模糊匹配的原則,前面的home會匹配到父親組件,所以跳轉到的路徑就 localhost:3000/home/news 了
- 這里就是為什么如果有子嵌套路由的話,就不要開始嚴格匹配的原因
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
因為 /home/news 路由一開始是從這三個最先注冊的路由開始匹配的,由于模糊匹配所以匹配上了Home,之后才到Home里面再進行子路由的匹配的
如果是嚴格匹配的話,那么就是直接<Redirect to="/about"/>
,直接就到了About組件了,所以就是從外層就屏蔽掉了,根本就進不去子路由了
[圖片上傳失敗...(image-3302a7-1651062725158)]
總結:
嵌套路由
1.注冊子路由時要寫上父路由的path值 2.路由的匹配是按照注冊路由的順序進行的
六、路由組件傳參
常見的組件路由傳遞參數的方式有三種,分別是通過params參數傳遞、利用search屬性傳遞、利用state屬性傳遞
1. 傳遞params參數
[圖片上傳失敗...(image-34b219-1651062725158)]
為什么通過props可以獲取到參數呢?我們不妨打印一下this.props
看一下此時的Details 組件到底接收了哪些信息。
我們可以看到,此時組件的props屬性中,有著三個屬性,分別是history
,location
和match,而
match`屬性中已經幫我們把傳遞的參數封裝成為一個對象了。
[圖片上傳失敗...(image-fdd407-1651062725158)]
2. 傳遞search參數
- 這種方式基本上和我們常見的在url地址上進行參數拼接是一樣的,
- 這種方式寫法較為簡單,只需要在 Link 標簽中定義好key=value形式參數拼接,
- 然后在實際的組件中,直接從prop屬性中獲取,調用querystring(或者其他處理字符串轉對象的函數庫)處理參數,并封裝成對象返回即可
[圖片上傳失敗...(image-535089-1651062725158)]
注意:
- 這種方式下的傳參并不需要額外對路由進行配置
props.location.search
屬性中把我們Link的參數都封裝了進去,但是這種方式封裝的參數是字符串形式的,我們并不能直接使用,而是要借助queryString
庫來幫助我們把字符串解析成對象。[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DszxSfhy-1651047151071)(C:\Users\ACER\AppData\Roaming\Typora\typora-user-images\image-20220424132909094.png)]
3. 傳遞state參數
- 和前兩種傳遞參數的方式不同,這種方式傳遞的參數是不會在瀏覽器中顯式的展示的,
- 同時,即使刷新頁面,路由子頁面的數據還是不會消失。
- 原因是react路由器幫我們對維護了history對象(history對象中又維護了location對象,所以也就有了state對象)
[圖片上傳失敗...(image-94f6a4-1651062725158)]
注意:
- 這種方式下的傳參并不需要額外對路由進行配置
location.state
屬性中,我們可以獲取到我們在Link中傳遞的參數。[圖片上傳失敗...(image-229b39-1651062725158)]
總結
向路由組件傳遞參數
1.params參數 路由鏈接(攜帶參數):`<Link to='/demo/test/tom/18'}>詳情</Link>` 注冊路由(聲明接收):`<Route path="/demo/test/:name/:age" component={Test}/>` 接收參數:this.props.match.params 2.search參數 路由鏈接(攜帶參數):<Link to='/demo/test?name=tom&age=18'}>詳情</Link> 注冊路由(無需聲明,正常注冊即可):`<Route path="/demo/test" component={Test}/>` 接收參數:this.props.location.search 備注:獲取到的search是urlencoded編碼字符串,需要借助querystring解析 3.state參數 路由鏈接(攜帶參數):`<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>詳情</Link>` 注冊路由(無需聲明,正常注冊即可):`<Route path="/demo/test" component={Test}/>` 接收參數:`this.props.location.state` 備注:刷新也可以保留住參數
七、多種路由跳轉方式
1. 路由跳轉的兩種模式 push與replace
push: a-b-c 跳轉會形成history,可返回到上一層
//push模式是棧的常規模式 跳轉操作進入入棧
// 例: this.props.history.push('路由地址')
replace: a-b-c 跳轉不會形成history,不可返回到上一層 適用于登錄后,不需要重新回到登頁面
//replace模式是替換模式,會替換掉棧頂的路由
//例: this.props.history.replace('路由地址')
2. 編程式路由導航
除了使用路由組件進行跳轉之外,其實我們自己也可以利用事件處理函數來走路由跳轉和參數傳遞的功能。
go & goBack & goForward
在路由組件中,我們可以通過this.prop.history
獲取到history對象,然后使用對象對應的API實現(歷史)路徑的跳轉。簡單理解的話,就是通過路由組件的history對象,實現頁面前進、后退的功能。
[圖片上傳失敗...(image-3edc0b-1651062725158)]
總結:
編程式路由導航
借助this.prosp.history對象上的API對操作路由跳轉、前進、后退 -this.prosp.history.push() -this.prosp.history.replace() -this.prosp.history.goBack() -this.prosp.history.goForward() -this.prosp.history.go()
3.通過history對象主動調用push/replace
方法
我們可以在路由組件中通過this.props.history
獲取到history對象后,通過主動調用push或者replace方法來進行路由的跳轉,需要注意的是,當通過編程式事務主動進行路由跳轉時,對應的參數需要和之前一樣,根據傳遞方式的不同來定義:
傳遞參數格式根據傳遞方式的不同來定義:
[圖片上傳失敗...(image-46603a-1651062725158)]
八、withRouter的使用
高階組件中的withRouter
, 作用是將一個組件包裹進Route
里面, 然后react-router
的三個對象history, location, match
就會被放進這個組件的props
屬性中.
// withRouter實現原理:
// 將組件包裹進 Route, 然后返回
// const withRouter = () => {
// return () => {
// return <Route component={Nav} />
// }
// }
// 這里是簡化版
const withRouter = ( Component ) => () => <Route component={ Component }/>
//withRouter的返回值是一個新組件
如果我們某個東西不是一個Router
, 但是我們要依靠它去進行瀏覽記錄的前進后退 這時候就可以使用withRouter
,將一般組件變成路由組件
import React, { Component } from 'react'
import {withRouter} from 'react-router-dom' //引入withRouter
class Header extends Component {
back = ()=>{
this.props.history.goBack()
}
forward = ()=>{
this.props.history.goForward()
}
go = ()=>{
this.props.history.go(-2)
}
render() {
console.log('Header組件收到的props是',this.props);
return (
<div className="page-header">
<h2>React Router Demo</h2>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前進</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
export default withRouter(Header)
//withRouter可以加工一般組件,讓一般組件具備路由組件所特有的API
//withRouter的返回值是一個新組件
九、前端路由的區別
BrowserRouter與HashRouter的區別
1.底層原理不一樣:
BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。 HashRouter使用的是URL的哈希值。
2.path表現形式不一樣
BrowserRouter的路徑中沒有#,例如:localhost:3000/demo/test HashRouter的路徑包含#,例如:localhost:3000/#/demo/test
3.刷新后對路由state參數的影響
(1).BrowserRouter沒有任何影響,因為state保存在history對象中。 (2).HashRouter刷新后會導致路由state參數的丟失!!!
4.備注:HashRouter可以用于解決一些路徑錯誤相關的問題。