網頁中最重要的一部分。
基本結構 附註:如果是 <= 4.20
的話得用 JSX 的寫法
會由 Menu
、Menu.Item
、Menu.SubMenu
和 Menu.ItemGroup
來組成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import React, { useState } from 'react' import { Menu } from 'antd' function Main ( ) { return ( <Menu mode ='horizontal' defaultSelectedKeys ={[ 'home ']}> <Menu.Item key ='home' > Home</Menu.Item > <Menu.Item key ='about' > About</Menu.Item > <Menu.SubMenu key ='products' title ='Products' > <Menu.ItemGroup title ='Type A' > <Menu.Item key ='a1' > ProductA-1</Menu.Item > <Menu.Item key ='a2' > ProductA-2</Menu.Item > <Menu.Item key ='a3' > ProductA-3</Menu.Item > </Menu.ItemGroup > <Menu.ItemGroup title ='Type B' > <Menu.Item key ='b1' > ProductB-1</Menu.Item > <Menu.Item key ='b2' > ProductB-2</Menu.Item > <Menu.Item key ='b3' > ProductB-3</Menu.Item > </Menu.ItemGroup > </Menu.SubMenu > <Menu.Item key ='contact' > Contact</Menu.Item > </Menu > ) } export default Main
出來的結果大概就像這樣
如果是 >= 4.20
的話,可以改用 props 的方式來寫(官方也推薦這樣用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 import { Menu } from 'antd' const items = [ { key : 'home' , label : 'Home' }, { key : 'about' , label : 'About' }, { key : 'producrs' , label : 'Products' , children : [ { type : 'group' , label : 'Type A' , children : [ { key : 'a1' , label : 'ProductA-1' }, { key : 'a2' , label : 'ProductA-2' }, { key : 'a3' , label : 'ProductA-3' } ] }, { type : 'group' , label : 'Type B' , children : [ { key : 'b1' , label : 'ProductB-1' }, { key : 'b2' , label : 'ProductB-2' }, { key : 'b3' , label : 'ProductB-3' } ] } ] }, { key : 'service' , label : 'Service' } ] export default function App ( ) { return <Menu items ={items} mode ='horizontal' defaultSelectedKeys ={[ 'home ']} /> }
至於 active 的狀態它會自己幫你處理,所以不用額外設定。
自己處理 active 狀態 如果你想要自己設定 active 狀態的話,可以用 selectedKeys
來處理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import React from 'react' import type { MenuProps } from 'antd' import { Menu } from 'antd' function Main ( ) { const [current, setCurrent] = React.useState<string []>(['home' ]) const handleClick: MenuProps['onClick' ] = (e ) => { console .log('click' , e.key) setCurrent([e.key]) } return ( <Menu mode ='horizontal' onClick ={handleClick} selectedKeys ={current} > <Menu.Item key ='home' > Home</Menu.Item > <Menu.Item key ='about' > About</Menu.Item > <Menu.SubMenu key ='products' title ='Products' > <Menu.ItemGroup title ='Type A' > <Menu.Item key ='a1' > ProductA-1</Menu.Item > <Menu.Item key ='a2' > ProductA-2</Menu.Item > <Menu.Item key ='a3' > ProductA-3</Menu.Item > </Menu.ItemGroup > <Menu.ItemGroup title ='Type B' > <Menu.Item key ='b1' > ProductB-1</Menu.Item > <Menu.Item key ='b2' > ProductB-2</Menu.Item > <Menu.Item key ='b3' > ProductB-3</Menu.Item > </Menu.ItemGroup > </Menu.SubMenu > <Menu.Item key ='contact' > Contact</Menu.Item > </Menu > ) } export default Main
跟 React router 結合起來 如果可以透過一份資料,就自動產生 Menu 跟 Router 的話是不是挺方便的?這個其實是有辦法做到的!這邊會先介紹 >= 4.20
的方法,比較簡潔一點。
假設 Menu 會有四個連結:
Home
Products
Service
About
那導覽列的部分就會這樣寫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const menu = [ { key : 'home' , label : 'Home' }, { key : 'products' , label : 'Products' }, { key : 'service' , label : 'Service' }, { key : 'about' , label : 'About' } ] fucntino Nav () { return <Menu items ={menu} /> }
路由的部分則會這樣寫:
1 2 3 4 5 6 7 8 9 10 function Main ( ) { return ( <Routes > <Route path ='home' element ={ <Home /> } /> <Route path ='products' element ={ <Products /> } /> <Route path ='service' element ={ <Service /> } /> <Route path ='about' element ={ <About /> } /> </Routes > ) }
好,接下來可以思考一件事:
有沒有辦法讓 menu 也能渲染 router?
你可以先想想看如果要讓 router 也能共用的話,還需要哪些資料?
想完後再拉下去看,最後會整理成像這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import { Link } from 'react-router-dom' import Home from './Home' import About from './About' import Prodcuts from './Prodcuts' import Service from './Service' const menu = [ { key : 'home' , path : '' , label : <Link to ='' > Home</Link > , component: <Home /> }, { key : 'products' , path : 'products' , label : <Link to ='products' > Products</Link > , component: <Products /> }, { key : 'service' , path : 'service' , label : <Link to ='service' > Service</Link > , component: <Service /> }, { key : 'about' , path : 'about' , label : <Link to ='' > About</Link > , component: <About /> } ]
首先需要讓導覽列上的按鈕可以連結到路由,所以把 label
的部分改成 <Link>
的形式。(如果希望不要有連結,可以保留為純文字就好)
接著 router 的部分因為要渲染出 <Route path="path" element={<Component />} />
的形式,所以加上 path
跟 component
的屬性。
資料 OK 以後,接著只要把渲染函式寫出來就可以了:
1 2 3 4 5 6 7 8 9 10 11 12 13 import { menu } from './menu' function renderRoute (routes ) { return routes.map((route ) => ( <Route key ={route.key} path ={route.path} element ={route.component} /> )) } function RouteComponent ( ) { return <Routes > {renderRoute(menu)}</Routes > }
做到這邊以後,就可以透過 menu
來自動生成導覽列跟路由了,想看範例的話到 這邊 看。
<= 4.20 的做法 如果是 <=4.20
的話,<Menu />
不能用 items
屬性,所以就要自己寫一個 generator 來產生:
1 2 3 4 5 6 7 8 9 function Nav ( ) { return ( <Menu > {menu.map((menu) => ( <Menu.Item key ={menu.key} > {menu.label}</Menu.Item > ))} </Menu > ) }
也沒有很複雜,只是順便提一下。
子路由與子導覽列 這邊只是做個補充,基本概念都跟上面一樣,只是資料更多層,還有渲染函式稍微複雜一點:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import React from 'react' import { Menu } from 'antd' import { Link, Outlet } from 'react-router-dom' import Home from '../pages/Home' import ProdcutA from '../pages/ProductA' import ProdcutB from '../pages/ProductB' export const items = [ { key : 'home' , url : '' , label : <Link to ='' > Home</Link > , component: <Home /> }, { key : 'products' , url : 'products' , label : 'Products' , component : <Outlet /> , children: [ { key : 'A' , url : 'productA/*' , label : <Link to ='products/productA' > ProdcutA</Link > , component: <ProdcutA /> }, { key : 'B' , url : 'productB/*' , label : <Link to ='products/productB' > ProdcutB</Link > , component: <ProdcutB /> } ] } ] const Header: React.FC = () => { return ( <> {/* <Menu mode ="horizontal" > {items.map((item: any) => item.children ? ( <Menu.SubMenu key ={item.key} title ={item.label} > {item.children.map((subItem: any) => ( <Menu.Item key ={subItem.key} > {subItem.label}</Menu.Item > ))} </Menu.SubMenu > ) : ( <Menu.Item key ={item.key} > {item.label}</Menu.Item > ) )} </Menu > */} <Menu mode ='horizontal' items ={items} /> </> ) } export default Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import { Routes, Route } from 'react-router-dom' import Header from './components/Header' import { items } from './components/Header' function renderRoute (routes: any ) { return routes.map((route: any ) => route.children ? ( route.children.map((subRoute: any ) => ( <Route key ={route.key} path ={route.url} element ={route.component} > <Route key ={subRoute.key} path ={subRoute.url} element ={subRoute.component} /> </Route > )) ) : ( <Route key ={route.key} path ={route.url} element ={route.component} /> ) ) } export default function App ( ) { return ( <> <Header /> <Routes > {renderRoute(items)}</Routes > </> ) }
想看範例的話一樣到 這邊 來看。