内容简介:React 是目前主流的前端开发框架,目前前端流行的框架是 Angular,Vue,React,具体选型看项目需求而定。antd 是基于 React 开发的组件库,有蚂蚁金服团队退出,目前使用人数较多,组件也比较多,文档也很友好。本次我做的就是使用 antd 的 Menu 组件搭配 React,实现浏览器地址改变,高亮对应导航菜单的需求。
React 是目前主流的前端开发框架,目前前端流行的框架是 Angular,Vue,React,具体选型看项目需求而定。
antd 是基于 React 开发的组件库,有蚂蚁金服团队退出,目前使用人数较多,组件也比较多,文档也很友好。
本次我做的就是使用 antd 的 Menu 组件搭配 React,实现浏览器地址改变,高亮对应导航菜单的需求。
具体实现
1.本次使用 React-router-dom@4.3.1 作为前端路由,为了方便,直接使用 HashRouter:
// 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.路由配置
//前端需要维护一份路由表,无论是静态配置亦或是由后端获取, 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' } ] } ] } ] } ]; 复制代码
-
组件编写
- 实现思路:
(1).为了实现不限级别的路由渲染及高亮菜单,我这里使用的是递归实现。
(2).当浏览器地址改变后要高亮对应菜单项,这个可能是一级菜单,也可能是子级菜单,所以还需要展开相应的子菜单
(3).监听浏览器地址变化,使用 react-router 渲染的组件在 props 中会接收 history 的对象,这个对象有一个 listen 方法,可以添加自定义监听事件,默认接收参数为一个对象:{ hash: "" pathname: "" search: "" state: undefined }
- 开始实现
// 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> ); } } 复制代码
-
效果图
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- FreeMarker对应各种数据结构解析
- Vue和MVVM的对应关系
- nhibernate一表对应多个实体类问题
- 调试V8中JS对应的汇编代码
- 二分查找及对应的几道经典题目
- 二分查找及对应的几道经典题目
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。