SoFunction
Updated on 2025-04-06

Vue3.0 implements unlimited menu

Business Requirements

Menu items are an important part of the business system. Generally, business systems must support displaying multi-level business menus, but according to the rights and responsibilities of each business personnel, the menu items they see are also different.

This requires that the page can support Infinitus menu display. According to the permissions of each user, the background service returns the corresponding menu item.

This article implements a configurable unlimited level menu based on Vue 3.0, with the key codes as follows:

The menu item data structure returned by the backend

Backend services generally do not directly return a tree-structured menu collection to the frontend, which is unreasonable. The front-end should build its own menu tree based on its specific needs. The data structure returned by the backend generally contains the following field:

  • Id Menu ID, number type
  • pId's parent menu ID, number type
  • title menu title
  • Links corresponding to the link menu
  • order The order of the menus of the same level, the number type

Other business fields require specific analysis of specific issues, and will not be discussed here. This article no longer discusses how the backend can control the permissions of menu items. The menu content used is included in a JSON file, see the appendix for details.

Menu content is a football data management system, including multi-level menus:

  • The first level menu has only one item, which is the ancestor nodes of all nodes.
  • The second level menu includes league management, club management and player management
  • The third-level menu includes CRUD of the secondary menu content.

Key Code

In order to support infinite menus, all key algorithms in this article are based on recursive implementation. Mainly including:

1. Convert backend data into a tree structure
2. Backend data sorting
3. Generate Vue routing data based on menu tree structure
4. Recursive calls of menu components

Convert backend data to tree structure

The actual parameter of the dataToTree function call is the appendix JSON data. This code refers to the AST tree conversion code of Vue 3.0. The specific idea is:

1. Divide the data of the collection into a parent node and a child node collection, and the outermost parent node is a node with pId of 0.
2. Find the direct child node of the current parent node in the child node and remove it from the current child node collection.
3. Recursively return to 1 and look for the child nodes of the child node.
4. If the current child node is not the parent node of any node, put the child node into the children collection of the parent node.

After generating the current tree structure menu data, the data can be saved in vuex for public data and is convenient for other modules to use.

function dataToTree(data) {
  const parents = ((item) =>  === 0);
  const children = ((item) =>  !== 0);
  toTree(parents, children);
  return parents;
  function toTree(parents, children) {
    for (var i = 0; i < ; ++i) {
      for (var j = 0; j < ; ++j) {
        if (children[j].pId === parents[i].Id) {
          let _children = deepClone(children, []);
          toTree([children[j]], _children);
          if (parents[i].children) {
            parents[i].(children[j]);
          } else {
            parents[i].children = [children[j]];
          }
        }
      }
    }
  }
}

function deepClone(source, target) {
  var _tar = target || {};
  let keys = (source);
  ((key) => {
    if (typeof source[key] === "object") {
      _tar[key] =
        (source[key]) === "[object Array]"
          ? []
          : {};
      deepClone(source[key], _tar[key]);
    } else {
      _tar[key] = source[key];
    }
  });
  return _tar;
}

Menu item sorting

Sort by order value of the same level node, this article does not put this sort and the tree structure transformation of the previous section together, mainly considering that some systems may not need sorting. If necessary, each time you add elements, you need to sort them, which is inefficient. Therefore, after obtaining the tree structure, you will sort them again. The specific sorting function is as follows:

function SortTree(tree) {
  tree = ((a, b) =>  - );
  ((t) => {
    if () {
       = SortTree();
    }
  });

  return tree;

Use the simplest recursive method to traverse the current tree collection and sort it in ascending order of the order field. If the current node has children items, sort it recursively.

Generate Vue routing data based on menu tree structure

After obtaining the tree menu, we can generate the routing items to be used by the user in the App based on the current data. The specific code is as follows:

function TreeToRoutes(treeData, routes) {
  routes = routes || [];
  for (var i = 0; i < ; ++i) {
    routes[i] = {
      path: treeData[i].link,
      name: treeData[i].name,
      component: () => import(`@/views/${treeData[i].name}`),
    };
    if (treeData[i].children) {
      routes[i].children = TreeToRoutes(
        treeData[i].children,
        routes[i].children
      );
    }
  }
  return routes;
}

1. Iterate through the tree menu, copy the link and tname of the current menu item to the path and name of the Vue route data, and the component adopts dynamic loading.
2. If the current menu item contains children children, recursively call it and copy its children's content.

In the method, the menu data is read through vuex, and then the above algorithm is called to generate routing data. Loading this data directly into Vue's route ensures that if the current user does not have permissions to a certain menu, it will not be accessible even if it is accessed through the URL, because the App will only generate routing data for the authorized menu items. If the user does not have permissions to a certain menu, the data from the menu will not be obtained from the backend, and the route will not be generated for the menu item.

Recursive calls to menu components

The menu component code is as follows:

<template>
  <div>
      <ul v-if=" &&  > 0">
          <li><router-link :to="">{{}}</router-link></li> 
          <menu-item :data="item" :key="index"  v-for="(item,index) in ">
      </ul>
      <ul v-else>
          <li><router-link :to="">{{}}</router-link></li> 
      </ul>
  </div>
</template>

<script>
export default {
    name: "MenuItem",
    props:{
        data: Object
    }
}
</script>

If the current menu item contains child nodes, the MenuItem component itself is called recursively

The code for the menu component calls is as follows:

<template>
  <div>
     <menu-item :data="item" :key="index" v-for="(item,index) in data" />
  </div>
</template>

<script>
import MenuItem from './MenuItem'
export default {
    name: "Page",
    components:{
        MenuItem
    }
}
</script>

Since the outermost layer of the generated menu data structure is data, the MenuItem component needs to be called cyclically.

Appendix - Menu item data

export default [
  {
    Id: 15,
    pId: 0,
    name: "all",
    title: "all",
    link: "/all",
    order: 2,
  },
  {
    Id: 1,
    pId: 15,
    name: "clubs",
    title: "Club Management",
    link: "/clubs",
    order: 2,
  },
  {
    Id: 2,
    pId: 15,
    name: "leagues",
    title: "League Management",
    link: "/leagues",
    order: 1,
  },
  {
    Id: 3,
    pId: 15,
    name: "players",
    title: "Player Management",
    link: "/players",
    order: 3,
  },
  {
    Id: 5,
    pId: 2,
    name: "LeagueDelete",
    title: "Delete League",
    link: "/leagues/delete",
    order: 3,
  },
  {
    Id: 6,
    pId: 2,
    name: "LeagueUpdate",
    title: "Update League",
    link: "/leagues/update",
    order: 2,
  },
  {
    Id: 7,
    pId: 2,
    name: "LeagueAdd",
    title: "Add League",
    link: "/leagues/add",
    order: 1,
  },
  {
    Id: 8,
    pId: 3,
    name: "PlayerAdd",
    title: "Add Player",
    link: "/players",
    order: 1,
  },
  {
    Id: 9,
    pId: 3,
    name: "PlayerUpdate",
    title: "Update Player",
    link: "/players",
    order: 3,
  },
  {
    Id: 10,
    pId: 3,
    name: "PlayerDelete",
    title: "Delete Player",
    link: "/players",
    order: 2,
  },
  {
    Id: 11,
    pId: 1,
    name: "ClubAdd",
    title: "Add Club",
    link: "/clubs/add",
    order: 3,
  },
  {
    Id: 12,
    pId: 1,
    name: "ClubUpdate",
    title: "Update Club",
    link: "/clubs/update",
    order: 1,
  },
  {
    Id: 13,
    pId: 1,
    name: "ClubDelete",
    title: "Delete Club",
    link: "/clubs/delete",
    order: 2,
  },
];

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.