ant design pro 代码学习(三) ----- 菜单数据分析

1、getSelectedMenuKeys 获取选中的菜单的key

  getSelectedMenuKeys = () => {
    const {location: {pathname}} = this.props;
    return getMeunMatchKeys(this.flatMenuKeys, urlToList(pathname));
  };

   getSelectedMenuKeys中涉及到getMeunMatchKeys()、this.flatMenuKeys、urlToList(),下边分别对其进行分析。

this.flatMenuKeys = getFlatMenuKeys(props.menuData);
.....
export const getFlatMenuKeys = menu =>
  menu.reduce((keys, item) => {
    keys.push(item.path);
    if (item.children) {
      return keys.concat(getFlatMenuKeys(item.children));
    }
    return keys;
  }, []);

  getFlatMenuKeys通过对props.menuData数据递归调用,返回props.menuData中所有层级数据的path值(Array类型),其中path为完整的路径值(getMenuData()已处理过)

export function urlToList(url) {
  //filter(i => i) 去除空
  const urllist = url.split('/').filter(i => i);
  return urllist.map((urlItem, index) => {
    return `/${urllist.slice(0, index + 1).join('/')}`;
  });
}

  将pathname传入到urlToList方法中,urlToList首先对url用’/’分割,filter方法对空值进行处理(分割后第一个元素为空)。返回一级路径、二级路径…依次类推。例如当前pathname为/A/B时,则返回[‘/A’,’/A/B’]

export const getMeunMatchKeys = (flatMenuKeys, paths) =>
  paths.reduce((matchKeys, path) => (
    matchKeys.concat(
      flatMenuKeys.filter(item => pathToRegexp(item).test(path))
    )), []);

  getMeunMatchKeys对flatMenuKeys、paths做双重循环,其中pathToRegexp会对/path/:id等数据做处理,会匹配/path路径。返回值为,flatMenuKeys中包含的paths元素(返回类型Array)。

  综上所述,getFlatMenuKeys返回当前路由的pathname对应在flatMenuKeys中匹配项。例如当前pathname=’/dashboard/analysis’(有效路由),则返回[‘/dashboard’,’/dashboard/analysis’];当前pathname=’/aaa’(无效路由),则返回[],因为flatMenuKeys找不到匹配项。


2、handleOpenChange 切换菜单时,更改选中的菜单key

isMainMenu = key => {
    return this.menus.some(item => key && (item.key === key || item.path === key));
};

handleOpenChange = openKeys => {
    const lastOpenKey = openKeys[openKeys.length - 1];
    const moreThanOne = openKeys.filter(openKey => this.isMainMenu(openKey)).length > 1;

    this.setState({
      openKeys: moreThanOne ? [lastOpenKey] : [...openKeys],
    });
};

......
//Menu组件中onOpenChange事件
onOpenChange={this.handleOpenChange}

   isMainMenu用来检测当前路径是否为一级菜单路径。

   由ant design中的Menu组件可知,onOpenChange事件的回调方法中,传入参数openKeys为当前多级路径、将被打开的路径的集合。

  1. 如果当前路径、即将打开的路径是一级菜单间切换,则moreThanOne必为true,例如当前’/dashboard/analysis’,即将打开’/list’,此时openKeys为[‘/dashboard’,’/dashboard/analysis’,’/list’],则openKeys为即将打开的页面的路由,即/list’;

  2. 如果是当前菜单的子菜单打开,则moreThanOne为false,例如当前打开的菜单时’/list’(列表项),即将打开’/list/search’(搜索列表),则openKeys为[“/list”, “/list/search”],此时Menu组件会打开:列表-搜索列表;

  3. 如果,点击当前打开的菜单,则此时即为关闭菜单,openKeys为[]。

  *注:结合getSelectedMenuKeys、handleOpenChange。对selectedKeys做了如下处理:如果根据当前路由信息找不到匹配的菜单项,则当前选中的菜单项为解决要打开的菜单项。*

let selectedKeys = this.getSelectedMenuKeys();
if (!selectedKeys.length) {
  selectedKeys = [openKeys[openKeys.length - 1]];
}

3、getNavMenuItems 、getSubMenuOrItem获取菜单项入口方法

  getNavMenuItems获取存在菜单名称(name)、且可见的菜单(!item.hideInMenu)。其中getSubMenuOrItem根据当前的是否存在子菜单返回或者

  getNavMenuItems = menusData => {
    if (!menusData) {
      return [];
    }
    return menusData
      .filter(item => item.name && !item.hideInMenu)
      .map(item => {
        // make dom
        const ItemDom = this.getSubMenuOrItem(item);
        return this.checkPermissionItem(item.authority, ItemDom);
      })
      .filter(item => item);
  };
 getSubMenuOrItem = item => {
    if (item.children && item.children.some(child => child.name)) {
      const childrenItems = this.getNavMenuItems(item.children);

      if (childrenItems && childrenItems.length > 0) {
        return (
          <SubMenu
            title={
              item.icon ? (
                <span>
                  {getIcon(item.icon)}
                  <span>{item.name}</span>
                </span>
              ) : (
                item.name
              )
            }
            key={item.path}
          >
            {childrenItems}
          </SubMenu>
        );
      }
      return null;
    } else {
      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
    }
  };

  第一次调用getSubMenuOrItem方法,

 <Menu>
 ......
  {this.getNavMenuItems(this.menus)}
</Menu>

有如下逻辑,遍及菜单数据(一级菜单):
1. 如果当前菜单数据(item),不存在子菜单或者子菜单中不存在name属性时,则判断为当前菜单项没有子菜单,返回Menu.Item;此时代表一级菜单没有子菜单,则返回的Menu.Item直接作为

的子元素;

  1. 如果当前菜单数据存在子菜单,则对子菜单调用getNavMenuItems方法,此处存在递归调用getSubMenuOrItem。继续执行1、2。当前子菜单如果没有下一级子菜单,则返回SubMenu - Menu.Item - SubMenu,如果存在继续递归调用。此时一级菜单已经确定含有子菜单,所以递归的结果是作为SubMenu的子元素,当然SubMenu中也有可能包含SubMenu。

4、checkPermissionItem 获取当前菜单的权限

  • 方法使用:
const ItemDom = this.getSubMenuOrItem(item);
return this.checkPermissionItem(item.authority, ItemDom);
  • 方法引用
  checkPermissionItem = (authority, ItemDom) => {
    if (this.props.Authorized && this.props.Authorized.check) {
      const {check} = this.props.Authorized;
      return check(authority, ItemDom);
    }
    return ItemDom;
  };

   由getSubMenuOrItem返回的是SubMenu或者Menu.Item组件,checkPermissionItem方法中的Authorized属性是组件,由ant design pro 代码学习(一) —– 路由分析分析可知,this.props.Authorized.check() 接受三个参数:1、组件的权限值;2、鉴权通过返回的组件;3、鉴权不通过返回的组件。方法内部会根据当前权限值authority与currentAuthority对比,决定返回值。

   由menuData中的数据可知,当前菜单数据中,除了账户(user)的authority为’guest’,其他均为undefined。而当前的权限值currentAuthority是根据调用接口’/api/login/account’来确定的(具体在后续登录部分在分析)登录后不为’guest’(具体根据账号类型为admin或者user)。

   check()中当传入的authority为undefined时,则直接认为是鉴权通过。由于账户(user)相关的权限为guest,不等于’admin’,此时返回undefined(因为鉴权未通过的组件参数未传入)。getNavMenuItems()方法中通过Array.filter过滤掉undefined、null、‘’ 等元素。否则的话根据react官方文档api可知,在jsx中undefined会被渲染为空

,会生成无效空元素。

false,null,undefined,和 true 都是有效的的 children(子元素) 。但是并不会被渲染。 —– react文档-深入 JSX


5、getMenuItemPath 生成菜单项

   根据菜单数据中的path属性,判断是否含有https,如果有,则认为是链接,生成a标签,如果没有则认为系统内部页面,则生成Link标签。getIcon用于生成菜单图标 。

 getMenuItemPath = item => {
    const itemPath = this.conversionPath(item.path);
    const icon = getIcon(item.icon);
    const {target, name} = item;
    // Is it a http link
    if (/^https?:\/\//.test(itemPath)) {
      return (
        <a href={itemPath} target={target}>
          {icon}
          <span>{name}</span>
        </a>
      );
    }
    return (
      <Link
        to={itemPath}
        target={target}
        replace={itemPath === this.props.location.pathname}
        onClick={
          this.props.isMobile
            ? () => {
              this.props.onCollapse(true);
            }
            : undefined
        }
      >
        {icon}
        <span>{name}</span>
      </Link>
    );
  };

6、菜单数据流程图

这里写图片描述


上一篇:ant design pro 代码学习(二) —– 路由数据分析
下一篇:ant design pro 代码学习(四) —– 数据mock


猜你喜欢

转载自blog.csdn.net/zcs425171513/article/details/80815373
今日推荐