React?路由使用示例詳解
Router
react-router-dom
是一個(gè)處理頁(yè)面跳轉(zhuǎn)的三方庫(kù),在使用之前需要先安裝到我們的項(xiàng)目中:
# npm npm install react-router-dom@6 #yarn yarn add react-router-dom@6
簡(jiǎn)單路由
使用路由時(shí)需要為組件指定一個(gè)路由的path
,最終會(huì)以path
為基礎(chǔ),進(jìn)行頁(yè)面的跳轉(zhuǎn)。具體使用先看個(gè)簡(jiǎn)單示例,該示例比較簡(jiǎn)單就是兩個(gè)Tab
頁(yè)面的來(lái)回切換。
///導(dǎo)入路由 import {Link} from 'react-router-dom' function App() { return ( <div> <h1>路由練習(xí)</h1> <nav> {/* link 頁(yè)面展示時(shí),是個(gè)a標(biāo)簽 */} <Link className ='link' to='/Tab1'> Tab1</Link> ///覆蓋:渲染tab1組件 <Link className = 'link' to='/Tab2'> Tab2 </Link> ///覆蓋:渲染tab2組件 </nav> </div> ); } ///路由頁(yè)面1 export default function Tab1(params) { return ( // 文檔中,<main> 元素是唯一的,所以不能出現(xiàn)一個(gè)以上的 <main> 元素 <main style={{ padding: "1rem 0" }}> <h2>我是Tab1</h2> </main> ); } ///路由頁(yè)面2 export default function Tab2(params) { return ( <main style={{ padding: "1rem 0" }}> <h2>我是Tab2</h2> </main> ); } ///在index.js中配置路由 import {BrowserRouter,Routes,Route} from 'react-router-dom' import Tab1 from './pages/Tab1.jsx' import Tab2 from './pages/Tab2.jsx' const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path = '/' element = {<App/>} /> ///兄弟路由 <Route path = '/Tab1' element = {<Tab1/>} />///兄弟路由 <Route path = '/Tab2' element = {<Tab2/>} />///兄弟路由 </Routes> </BrowserRouter> </React.StrictMode> );
最終交互時(shí),上述路由配置會(huì)出現(xiàn)彼此覆蓋的情況,如下圖:
為了保證App
組件,不會(huì)在Tab1
和Tab2
切換時(shí)被覆蓋需要使用嵌套路由。
嵌套路由
嵌套路由,可以保證子路由共享父路由的界面而不會(huì)覆蓋。為此React
提供了Outlet
組件,將其用于父組件中可以為子路由的元素占位,并最終渲染子路由的元素。
Outlet
渲染一個(gè)子路由的元素
import {Link,Outlet} from 'react-router-dom' function App() { return ( <div> <h1>路由練習(xí)</h1> <nav> {/* link 頁(yè)面展示時(shí),是個(gè)a標(biāo)簽 */} <Link className ='link' to='/Tab1'> Tab1</Link> <Link className ='link' to='/Tab2'> Tab2 </Link> </nav> {/* 此時(shí)尚不能實(shí)現(xiàn)共享APP UI的同時(shí)渲染出 Tab1 和 Tab2,還需要使用 <Outlet/> 保證父路由,在子路由交換時(shí),仍然存在 */} <Outlet/> </div> ); } ///index.js const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path='/' element={<App />} > {/* 孩子路由,url為: / + 孩子的path */} <Route path='Tab1' element={<Tab1 />} /> <Route path='Tab2' element={<Tab2 />} /> </Route> </Routes> </BrowserRouter> </React.StrictMode> );
最終效果如下圖:
未匹配路由
通過(guò)path='*'
,實(shí)現(xiàn)沒(méi)有其他路由匹配時(shí),對(duì)其進(jìn)行匹配。
root.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path='/' element={<App />} > {/* 孩子路由,url為: / + 孩子的path */} <Route path='Tab1' element={<Tab1 />} /> <Route path='Tab2' element={<Tab2 />} /> <Route path = '*' element={<p> 未匹配到路由時(shí),會(huì)跳轉(zhuǎn)此處。 </p>} /> </Route> </Routes> </BrowserRouter> </React.StrictMode> );
效果如圖:
路由傳參數(shù)
通過(guò)路由傳遞參數(shù)到組件中
///模擬數(shù)據(jù) const dataList = [ { id:20220101, content:'筆記1' }, { id:20220102, content:'筆記2' }, { id:20220103, content:'筆記3' }, ] export default function getTodoList(params) { return dataList } export function findTodoItem(params) { return dataList.find((value)=>value.id === params) } ///組件Tab2中定義列表 export default function Tab2(params) { let list = getTodoList() return ( <div> <ul> { list.map((item) => ( <li key={item.id}> {/*子路由形如:'/Tab2/20220103' */} <Link to={`/Tab2/${item.id}`}>{item.content}</Link> </li> )) } </ul> {/*渲染一個(gè)子路由的元素*/} <Outlet /> </div> ); } ///注冊(cè)列表項(xiàng)的子路由 root.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path='/' element={<App />} > {/* 孩子路由,url為: / + 孩子的path */} <Route path='Tab1' element={<Tab1 />} /> <Route path='Tab2' element={<Tab2 />} > <Route path=':itemId' element={<ItemDetail/>}/> </Route> <Route path = '*' element={<p>未匹配到該路由請(qǐng)先設(shè)置路由頁(yè)面 </p>} /> </Route> </Routes> </BrowserRouter> </React.StrictMode> ); ///定義Tab2子組件 ItemDetail import { useParams } from 'react-router-dom' export function ItemDetail() { //點(diǎn)擊每一項(xiàng)的鏈接,注意:URL 發(fā)生了變化,但新的組件尚未顯示 ///需要父組件中添加<Outlet> ///HOOK 獲取路由中的參數(shù),形如{itemId:'20220102'} let params = useParams() let content = findTodoItem(parseInt(params.itemId)).content return ( <div> <h2>筆記詳情</h2> <p>這是我于{params.itemId},記錄的筆記他的內(nèi)容為{content}</p> </div> ) }
最終效果:
索引路由
當(dāng)我們切換至Tab1
再切回Tab2
后,筆記詳情頁(yè)面將空白,效果如下:
可以通過(guò)索引路由填補(bǔ)空白,具體只需:
root.render( <React.StrictMode> <BrowserRouter> <Routes> <Route path='/' element={<App />} > {/* 孩子路由,url為: / + 孩子的path */} <Route path='Tab1' element={<Tab1 />} /> <Route path='Tab2' element={<Tab2 />} > {/*索引路由 有index 無(wú)path*/} <Route index element={<p>請(qǐng)選擇一個(gè)筆記查看它的詳情 </p>}/> <Route path=':itemId' element={<ItemDetail/>}/> </Route> <Route path = '*' element={<p>未匹配到該路由請(qǐng)先設(shè)置路由頁(yè)面 </p>} /> </Route> </Routes> </BrowserRouter> </React.StrictMode> );
如此當(dāng)我們重復(fù)上述操作時(shí)便會(huì)呈現(xiàn)如下效果:
當(dāng)父路由匹配,但其他子路由都不匹配時(shí),由索引路由匹配。索引路由是父路由的默認(rèn)子路由。 當(dāng)用戶尚未單擊導(dǎo)航列表中的一項(xiàng)時(shí),會(huì)呈現(xiàn)索引路由。
活動(dòng)鏈接
與Link
功能一致,差異是可以設(shè)置點(diǎn)擊后的顏色
export default function Tab2(params) { let list = getTodoList() return ( <div> <ul> { list.map((item) => ( <li key={item.id}> {/* <Link to={`/Tab2/${item.id}`}>{item.content}</Link> */} <NavLink style = { ({isActive})=> ({ color : isActive ? "red" : "" }) } to={`/Tab2/${item.id}`}> {item.content} </NavLink> </li> )) } </ul> <Outlet /> </div> ); }
搜索參數(shù)
搜索參數(shù)類(lèi)似于 URL 參數(shù),形如/login?success=1
export default function Tab2(params) { let list = getTodoList() ///和React.useState很像 let [searchParams, setSearchParams] = useSearchParams(); return ( <div> {/* 搜索框: 隨著輸入設(shè)置搜索參數(shù) */} <input type="text" onChange = { (event)=>{ let text = event.target.value if (text) { setSearchParams({text}) } else { setSearchParams({}) } } } /> <ul> { list.filter((item)=>{ let txt = searchParams.get('text') if (!txt) return true return item.content.startsWith(txt) }) .map((item) => ( <li key={item.id}> {/* <Link to={`/Tab2/${item.id}`}>{item.content}</Link> */} <NavLink style = { ({isActive})=> ({ color : isActive ? "red" : "" }) } to={`/Tab2/${item.id}`}> {item.content} </NavLink> </li> )) } </ul> <Outlet /> </div> ); }
隨著我們輸入apple
, 路由的地址將變?yōu)?code>/Tab2?text=apple觸發(fā)路由重新呈現(xiàn)。
當(dāng)我們?cè)谳斎肟蜉斎胱址麜r(shí),便會(huì)觸發(fā)列表的過(guò)濾顯示:
自定義行為
上述UI在交互過(guò)程中,當(dāng)我們點(diǎn)擊Tab1
和Tab2
進(jìn)行切換時(shí),或者點(diǎn)擊apple
或appet
時(shí),會(huì)出現(xiàn)輸入框被清空,且列表不再被過(guò)濾的問(wèn)題。
react-router
提供了useLocation
方法,它返回瀏覽器顯示的url
信息。通過(guò)它可以獲取瀏覽器url
中的搜索參數(shù),從而進(jìn)行暫存,在具體組件內(nèi),可以通過(guò)useSearchParams
獲取到暫存的值。具體方式,通過(guò)自定義組件包裝NavLink
或Link
來(lái)實(shí)現(xiàn)。
///1.自定義`QueryLink`組件 import React, { Component } from 'react'; import { useLocation,NavLink } from "react-router-dom"; export default function QueryLink({to,...props}) { ///代表當(dāng)前瀏覽器顯示的url // location內(nèi)容:{pathname: '/Tab2', search: '?text=ad', hash: '', state: null, key: 'dhvg8xme'} let location = useLocation() // to = '/Tab2?text=ad' return <NavLink to={to+location.search} {...props} /> } //2. 替換 `tab1`、`tab2`的link or Navlink <QueryLink className ='link' to='/Tab1'> Tab1</QueryLink> <QueryLink className ='link' to='/Tab2'> Tab2 </QueryLink> //3. 替換列表項(xiàng)的link or Navlink <li key={item.id}> <QueryLink style = { ({isActive})=> ({ color : isActive ? "red" : "" }) } to={`/Tab2/${item.id}`}> {item.content} </QueryLink> </li>
useNavigate
上述示例中,路由的切換采用Link
或者NavLink
,但當(dāng)我們的頁(yè)面元素不使用Link
時(shí),比如使用Button
,此時(shí)便需要使用采用useNavigate
。同上可以配合useLocation
保存搜索字段。
export function ItemDetail() { //點(diǎn)擊每一項(xiàng)的鏈接,注意:URL 發(fā)生了變化,但新的組件尚未顯示 ///需要父組件中添加<Outlet> let params = useParams() let content = findTodoItem(parseInt(params.itemId)).content ///返回函數(shù) let navigate = useNavigate() ///獲取搜索字段 let location = useLocation() return ( <div> <h2>筆記詳情</h2> <p>這是我于{params.itemId}記錄的筆記,內(nèi)容為{content}</p> <button onClick = { (e)=>{ deleteTodoItem(params.itemId) navigate('/Tab2/'+location.search) } }> 刪除筆記 </button> </div> ) } // src/data.jsx export function deleteTodoItem(params) { dataList = dataList.filter((value)=>value.id !== parseInt(params)) }
參考資料
到此這篇關(guān)于React 路由使用的文章就介紹到這了,更多相關(guān)React 路由使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
到此這篇關(guān)于React 路由使用示例詳解的文章就介紹到這了,更多相關(guān)React 路由使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React?Hooks之useDeferredValue鉤子用法示例詳解
useDeferredValue鉤子的主要目的是在React的并發(fā)模式中提供更流暢的用戶體驗(yàn),特別是在有高優(yōu)先級(jí)和低優(yōu)先級(jí)更新的情況下,本文主要講解一些常見(jiàn)的使用場(chǎng)景及其示例2023-09-0930行代碼實(shí)現(xiàn)React雙向綁定hook的示例代碼
本文主要介紹了30行代碼實(shí)現(xiàn)React雙向綁定hook的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04嘗試自己動(dòng)手用react來(lái)寫(xiě)一個(gè)分頁(yè)組件(小結(jié))
本篇文章主要介紹了嘗試自己動(dòng)手用react來(lái)寫(xiě)一個(gè)分頁(yè)組件(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02React Native 集成jpush-react-native的示例代碼
這篇文章主要介紹了React Native 集成jpush-react-native的示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08React Native基礎(chǔ)入門(mén)之調(diào)試React Native應(yīng)用的一小步
這篇文章主要給大家介紹了關(guān)于React Native基礎(chǔ)入門(mén)之調(diào)試React Native應(yīng)用的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-07-07