React 是目前主流的前端開發框架,目前前端流行的框架是 Angular,Vue,React,具體選型看項目需求而定。javascript
antd 是基於 React 開發的組件庫,有螞蟻金服團隊退出,目前使用人數較多,組件也比較多,文檔也很友好。css
本次我作的就是使用 antd 的 Menu 組件搭配 React,實現瀏覽器地址改變,高亮對應導航菜單的需求。前端
1.本次使用React-router-dom@4.3.1做爲前端路由,爲了方便,直接使用 HashRouter:java
// Layout/index.js
//引入必要的組件
import React, { PureComponent } from 'react';
import { NavLink, Route, Switch, Redirect, Link } from 'react-router-dom';
import { Menu, Icon } from 'antd';
import 'assets/layout/index.scss';
const MenuItem = Menu.Item;
const SubMenu = Menu.SubMenu;
複製代碼
2.路由配置node
//前端須要維護一份路由表,不管是靜態配置亦或是由後端獲取, antd的Menu組件使用key做爲菜單項的惟一標識,這裏咱們直接使用path做爲key(若是是子菜單則使用title做爲key),,當瀏覽器hash改變後能夠更方便的獲取到菜單項(注意,key必定不要重複,不然達不到效果)
//這裏的菜單配置裏子菜單能夠任意級
const menuConfig = [
{
title: '首頁',
icon: 'pie-chart',
path: '/home'
},
{
title: '購買',
icon: null,
children: [
{
title: '詳情',
icon: null,
path: '/buy/detail'
}
]
},
{
title: '管理',
icon: null,
children: [
{
title: '業績',
icon: null,
children: [
{
title: '價格',
icon: null,
path: '/management/detail/price',
children: [
{
title: '股價',
icon: null,
path: '/management/ddd'
}
]
}
]
}
]
}
];
複製代碼
組件編寫react
(1).爲了實現不限級別的路由渲染及高亮菜單,我這裏使用的是遞歸實現。後端
(2).當瀏覽器地址改變後要高亮對應菜單項,這個多是一級菜單,也多是子級菜單,因此還須要展開相應的子菜單瀏覽器
(3).監聽瀏覽器地址變化,使用 react-router 渲染的組件在 props 中會接收 history 的對象,這個對象有一個 listen 方法,能夠添加自定義監聽事件,默認接收參數爲一個對象:{ hash: "" pathname: "" search: "" state: undefined }antd
// 1.先定義一個菜單節點類,在下面初始化路由表數據的時候會用到:
class MenuNode {
constructor(menuItem, parent = null) {
this.key = menuItem.path || menuItem.title;
this.parent = parent;
}
}
// 2. react組件
// defaultOpenKeys和defaultSelectedKeys是傳遞給Menu組件,用於指定當前打開的菜單項
export default class Index extends PureComponent {
constructor(props) {
super(props);
this.state = {
defaultOpenKeys: [],
defaultSelectedKeys: []
};
this.menuTree = [];
}
componentDidMount = () => {
const history = this.props.history;
//初始化路由表:
this.initMenu(menuConfig);
//在渲染完成後須要手動執行一次此方法設置當前菜單,由於此時不會觸發history的listen函數
this.setActiveMenu(history.location);
this.unListen = history.listen(this.setActiveMenu);
};
componentWillUnmount = () => {
//移除監聽
this.unListen();
};
//序列化路由表
initMenu = (config, parent = null) => {
for (let menuItem of config) {
if (menuItem.children) {
//若是menuItem有children則對其children遞歸執行此方法,而且將當前menuItem做爲父級
this.initMenu(menuItem.children, new MenuNode(menuItem, parent));
} else {
//若是這個路由不是沒有children,則是一級路由,則直接放入menuTree中
this.menuTree.push(new MenuNode(menuItem, parent));
}
}
//menuTree中最終存儲的是單個menuNode對象,經過判斷menuNode是否有效的parent便可判斷是一級路由仍是子菜單下的路由
};
//這個方法是實現菜單高亮的核心方法
setActiveMenu = location => {
//拿到當前瀏覽器的hash路徑
const pathname = location.pathname;
//
for (let node of this.menuTree) {
//使用正則判斷當前瀏覽器path是否與菜單項中的key相匹配,此正則能夠匹配動態路徑(相似於/product/:id這種傳參的路由),因此即使是動態路由也能高亮對應菜單
const isActivePath = new RegExp(`^${node.key}`).test(pathname);
if (isActivePath) {
const openKeys = [];
const selectedKeys = [node.key];
//判斷當前菜單是否有父級菜單,若是有父級菜單須要將其展開
while (node.parent) {
openKeys.push(node.parent.key);
node = node.parent;
}
this.setState({
defaultOpenKeys: openKeys,
defaultSelectedKeys: selectedKeys
});
return;
}
}
//若是一個路由都沒有匹配上則關閉菜單
this.setState({
defaultSelectedKeys: [],
defaultOpenKeys: []
});
};
//用於渲染路由,經過遞歸實現任意層級渲染
renderMenuItem = menuArr => {
const ret = menuArr.map(item => {
if (item.children) {
return (
<SubMenu title={item.title} key={item.path || item.title}> {this.renderMenuItem(item.children)} </SubMenu>
);
} else {
return (
<MenuItem title={item.title} key={item.path}> <Link to="item.path">{item.title}</Link> </MenuItem>
);
}
});
return ret;
};
render() {
return (
<div> <div> <div style={{ width: 150 }}> <div>當前菜單:{this.state.defaultSelectedKeys[0]}</div> <Menu mode="inline" theme="dark" selectedKeys={this.state.defaultSelectedKeys} openKeys={this.state.defaultOpenKeys} onSelect={this.selectMenuItem} onOpenChange={this.openMenu} > {this.renderMenuItem(menuConfig)} </Menu> </div> </div> <div id="main"> heelo,react </div> </div>
);
}
}
複製代碼
效果圖react-router