React中項(xiàng)目路由配置與跳轉(zhuǎn)方法詳解
最近使用 React 和 TS 搭建了一個(gè)項(xiàng)目,組件庫使用的是 Antd5。
在配置路由的過程中出現(xiàn)了一些小問題,所以記錄一下這個(gè)過程。
路由配置集中管理
首先,安裝 react-router-dom 包:
npm i react-router-dom
在 main.tsx 中,引入 BrowserRouter 并包裹在 App 組件外層:
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from "react-router-dom";
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<BrowserRouter>
<App />
</BrowserRouter>
);接下來,在 src/routes 目錄下創(chuàng)建 index.tsx 文件,引入所需依賴,并將組件映射到對(duì)應(yīng)路由上,讓 Router 知道在哪里渲染它們,如下:
import { lazy } from "react";
import { Outlet } from 'react-router-dom';
export interface RouteType {
path: string;
element: React.ReactNode;
children?: Array<RouteType>;
}
const Index = lazy(() => import('@/views/Index/index'));
const Information = lazy(() => import('@/views/personal/Information/index'));
const Contacts = lazy(() => import('@/views/personal/Contacts/index'));
const routes: Array<RouteType> = [
{
path: '/index',
element: <Index />,
}
{
path: '/personal',
element: <><Outlet></>,
children: [
{
path: 'information',
element: <Information />
},
{
path: 'contacts',
element: <Contacts />
}
]
}
];
export default routes;在上述代碼中,使用 React.lazy() 方法實(shí)現(xiàn)路由組件懶加載。
路由懶加載就是把不同的路由組件分割成不同的代碼塊,只有在路由被訪問的時(shí)候,才會(huì)加載對(duì)應(yīng)組件,其實(shí)就是按需加載。使用方法如下:
() => import(`@/views/${component}`)如果在某個(gè)路由下有子路由,通過 children 屬性配置,就是上面代碼中的這段:
const routes: Array<RouteType> = [
// ...
{
path: '/personal',
element: <><Outlet></>,
children: [
{
path: 'information',
element: <Information />
},
{
path: 'contacts',
element: <Contacts />
}
]
}
];其中,我們需要在渲染子組件的位置使用 Outlet 組件占位,當(dāng)然也可以使用 useOutlet(),它會(huì)返回該路由組件的子路由元素。
由于我使用 Fragment 作為子組件的容器,所以直接把 Outlet 放在這里占位。
一般情況下,父組件中還會(huì)有其他元素,此時(shí)我們需要這樣設(shè)置:
import { Outlet } from 'react-router-dom';
export const Information = () => {
return (
<Content>
// ...
<Outlet /> // 子組件渲染出口
</Content>
)
}最后,使用 useRoutes() 動(dòng)態(tài)配置路由。
useRoutes() 是 React Router V6 的一個(gè) Hook,它接收一個(gè)路由數(shù)組(也就是 routes 數(shù)組),根據(jù)匹配到的路由渲染相應(yīng)的組件。
import { useRoutes } from 'react-router-dom'; // 引入 useRoutes
// ...
const WrappedRoutes = () => {
return useRoutes(routes);
};
export default WrappedRoutes;到此,路由的集中管理就配置完畢。我們?cè)?app.tsx 文件中引入 WrapperRoutes 就可以使用了。
import WrappedRoutes from '@/router/index';
const App: React.FC = () => {
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider>
<div style={{ height: 90 }} />
<Menu /> // 側(cè)邊導(dǎo)航欄
</Sider>
<Layout>
<Content style={{ margin: 16 }}>
<WrappedRoutes /> // 渲染路由組件的位置
</Content>
</Layout>
</Layout>
)
};/routes/index.tsx 完整代碼如下:
import { lazy } from "react";
import { Outlet, useRoutes } from 'react-router-dom';
export interface RouteType {
path: string;
element: React.ReactNode;
children?: Array<RouteType>;
}
const Index = lazy(() => import('@/views/Index/index'));
const Information = lazy(() => import('@/views/personal/Information/index'));
const Contacts = lazy(() => import('@/views/personal/Contacts/index'));
const routes: Array<RouteType> = [
{
path: '/index',
element: <Index />,
}
{
path: '/personal',
element: <><Outlet></>,
children: [
{
path: 'information',
element: <Information />
},
{
path: 'contacts',
element: <Contacts />
}
]
}
];
const WrappedRoutes = () => {
return useRoutes(routes);
};
export default WrappedRoutes;如果不使用 useRoutes(),上述功能使用 Routes 和 Route 也可以實(shí)現(xiàn):
import { lazy } from "react";
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
const Index = lazy(() => import('@/views/Index/index'));
const Information = lazy(() => import('@/views/personal/Information/index'));
const Contacts = lazy(() => import('@/views/personal/Contacts/index'));
// ...
const App: React.FC = () => {
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider>
<div style={{ height: 90 }} />
<Menu /> // 側(cè)邊導(dǎo)航欄
</Sider>
<Layout>
<Content style={{ margin: 16 }}>
// 渲染路由組件的位置,用 Routes 包裹
<Routes>
<Route path="/" element={<Navigate to="/index" />} />
<Route path="index" element={<Index />} />
<Route path="personal" element={<><Outlet /></>} >
<Route path="information" element={<Information />} />
<Route path="contacts" element={<Contacts />} />
</Route>
</Routes>
</Content>
</Layout>
</Layout>
)
}注意:V6 中的嵌套路由可以只定義相對(duì)父路由的相對(duì)路徑,內(nèi)部會(huì)為我們自動(dòng)拼接全路徑。
Menu 組件實(shí)現(xiàn)路由跳轉(zhuǎn)
導(dǎo)航菜單使用的是 Menu 組件。
首先,定義一個(gè)類型為 Array<MenuItem> 的 menuList,用于導(dǎo)航菜單的展示。
// ...
import WrappedRoutes from '@/router/index'; // 引入路由表
type MenuItem = {
label: string,
key: string,
icon?: React.ReactNode
children?: Array<MenuItem>
}
const menuList: Array<MenuItem> = [{
label: "首頁",
key: "/index",
icon: <PieChartOutlined rev={undefined} />,
}, {
label: "個(gè)人辦公",
key: "/personal",
icon: <PushpinOutlined rev={undefined} />,
children: [
{
label: "個(gè)人信息",
icon: <IdcardOutlined rev={undefined} />,
key: "/personal/information"
},
{
label: "通訊錄",
icon: <ContactsOutlined rev={undefined} />,
key: "/personal/contacts"
}
]
}]
const App: React.FC = () => {
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider>
<div style={{ height: 90 }} />
<Menu theme="dark" mode="inline" items={menuList} />
</Sider>
<Layout>
<Content style={{ margin: 16 }}>
<WrappedRoutes />
</Content>
</Layout>
</Layout>
)
};
export default App;
此時(shí)點(diǎn)擊菜單項(xiàng)不能跳轉(zhuǎn)到對(duì)應(yīng)的路由,怎么實(shí)現(xiàn)呢?
在 Menu 組件上可以綁定點(diǎn)擊事件,通過它可以獲取 key、keyPath 等屬性。
const App = () => {
const handleClick = (e: any) => {
console.log(e);
console.log('key', e.key);
}
return (
// ...
<Menu theme="dark" mode="inline" items={menuList} onClick={handleClick} />
)
}
點(diǎn)擊 “個(gè)人信息” 菜單項(xiàng),可以看到,key 屬性就是我們要跳轉(zhuǎn)到的路由組件對(duì)應(yīng)的 url。
接下來,我們?cè)?handleClick 函數(shù)中使用 useNavigate(),將當(dāng)前 event 對(duì)象的 key 屬性傳入,即可根據(jù)傳入的 url 實(shí)現(xiàn)路由跳轉(zhuǎn)。
同時(shí),為了在全局環(huán)境下保持當(dāng)前選項(xiàng)的激活狀態(tài),需要使用 useLocation() 實(shí)時(shí)獲取并保存當(dāng)前 url,并交給 Menu 組件的 selectedKeys 屬性,它就是用于保存當(dāng)前激活的菜單選項(xiàng)。
import { useLocation, useNavigate } from 'react-router-dom';
const App = () => {
const navigate = useNavigate();
const { pathname } = useLocation(); // 獲取當(dāng)前url
const handleClick = (e: any) => {
navigate(e.key); // 實(shí)現(xiàn)跳轉(zhuǎn)
}
return (
// ...
<Menu theme="dark" selectedKeys={[pathname]} mode="inline"
items={menuList} onClick={handleClick} />
)
}
除去組件庫的引入,這部分功能的完整代碼如下:
import WrappedRoutes from '@/router/index'; // 引入路由表
import { useLocation, useNavigate } from 'react-router-dom';
type MenuItem = {
label: string,
key: string,
icon?: React.ReactNode
children?: Array<MenuItem>
}
const menuList: Array<MenuItem> = [{
label: "首頁",
key: "/index",
icon: <PieChartOutlined rev={undefined} />,
}, {
label: "個(gè)人辦公",
key: "/personal",
icon: <PushpinOutlined rev={undefined} />,
children: [
{
label: "個(gè)人信息",
icon: <IdcardOutlined rev={undefined} />,
key: "/personal/information"
},
{
label: "通訊錄",
icon: <ContactsOutlined rev={undefined} />,
key: "/personal/contacts"
}
]
}]
const App: React.FC = () => {
const navigate = useNavigate();
const { pathname } = useLocation(); // 獲取當(dāng)前url
const handleClick = (e: any) => {
// console.log(e);
// console.log('key', e.key);
navigate(e.key); // 實(shí)現(xiàn)跳轉(zhuǎn)
}
return (
<Layout style={{ minHeight: '100vh' }}>
<Sider>
<div style={{ height: 90 }} />
<Menu theme="dark" selectedKeys={[pathname]} mode="inline"
items={menuList} onClick={handleClick} />
</Sider>
<Layout>
<Content style={{ margin: 16 }}>
<WrappedRoutes />
</Content>
</Layout>
</Layout>
)
};
export default App;Breadcrumb 組件動(dòng)態(tài)切換
首先,根據(jù)上面配置的 menuList 實(shí)現(xiàn)一個(gè) getBreadcrumbNameMap 函數(shù),生成 key 和 label 的映射關(guān)系。
export const breadcrumbMap: Map<string, string> = new Map();
// 因?yàn)樽勇酚勺疃嘀挥幸粚?,使用雙循環(huán)簡(jiǎn)單實(shí)現(xiàn)功能。
function getBreadcrumbNameMap(breadcrumbMap: Map<string, string>) {
for (let menuItem of menuList) {
breadcrumbMap.set(menuItem.key, menuItem.label);
if (menuItem.children) {
for (let child of menuItem.children) {
breadcrumbMap.set(child.key, child.label);
}
}
}
}
console.log(breadcrumbMap);
我們已經(jīng)使用 useLocation 保存了當(dāng)前路由組件對(duì)應(yīng)的 url,也就是 pathname。
將得到的 pathname 以 / 字符分割并逐節(jié)遍歷,在 breadcrumbMap 中尋找對(duì)應(yīng)的 label,找到了就拼接下一段,繼續(xù)在 breadcrumbMap 中匹配 label,以此類推,直到遍歷完成。
const App = () => {
const { pathname } = useLocation();
const pathSnippets = pathname.split('/').filter((i) => i);
const breadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
console.log(url);
return {
key: url,
title: <Link to={url}>{breadcrumbMap.get(url)}</Link>,
};
});
return (
// ...
<Content style={{ margin: 16, padding: 12, minHeight: '95vh', backgroundColor: '#fff' }}>
<Breadcrumb style={{ marginBottom: 20 }} separator="/" items={breadcrumbItems} />
<WrappedRoutes />
</Content>
)
}
最后即可成功獲取當(dāng)前路由對(duì)應(yīng)的面包屑導(dǎo)航,效果如上。
以上就是React中項(xiàng)目路由配置與跳轉(zhuǎn)方法詳解的詳細(xì)內(nèi)容,更多關(guān)于React路由配置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決React報(bào)錯(cuò)The?tag?is?unrecognized?in?this?browser
這篇文章主要為大家介紹了解決React報(bào)錯(cuò)The?tag?is?unrecognized?in?this?browser示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解
這篇文章主要為大家介紹了React結(jié)合Drag?API實(shí)現(xiàn)拖拽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
React?Native中原生實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入的示例詳解
在React?Native社區(qū)中,原生動(dòng)態(tài)導(dǎo)入一直是期待已久的功能,在這篇文章中,我們將比較靜態(tài)和動(dòng)態(tài)導(dǎo)入,學(xué)習(xí)如何原生地處理動(dòng)態(tài)導(dǎo)入,以及有效實(shí)施的最佳實(shí)踐,希望對(duì)大家有所幫助2024-02-02
深入理解React中es6創(chuàng)建組件this的方法
this的本質(zhì)可以這樣說,this跟作用域無關(guān)的,只跟執(zhí)行上下文有關(guān)。接下來通過本文給大家介紹React中es6創(chuàng)建組件this的方法,非常不錯(cuò),感興趣的朋友一起看看吧2016-08-08
React使用Echarts/Ant-design-charts的案例代碼
這篇文章主要介紹了React使用Echarts/Ant-design-charts的實(shí)例代碼,本文通過實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11
關(guān)于React動(dòng)態(tài)加載路由處理的相關(guān)問題
這篇文章主要介紹了關(guān)于React動(dòng)態(tài)加載路由處理的相關(guān)問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
詳解react如何實(shí)現(xiàn)復(fù)合組件
在一些react項(xiàng)目開發(fā)中,常常會(huì)出現(xiàn)一些組合的情況出現(xiàn),這篇文章主要為大家介紹了復(fù)合組件的具體實(shí)現(xiàn),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-10-10

