Home  >  Article  >  Web Front-end  >  How to load dynamic menu in Vue3

How to load dynamic menu in Vue3

PHPz
PHPzforward
2023-05-10 13:58:062067browse

1. Overall idea

First of all, let’s sort out the overall implementation idea. First of all: The overall idea is exactly the same as vhr.

Considering that some friends may have forgotten the implementation idea of ​​the front-end dynamic menu in vhr, this article will analyze it with you.

In order to ensure that the menu data can be accessed in all .vue files, we choose to store the menu data in vuex. vuex is a common place for storing data in vue. All .vue files can read data from vuex. The data stored in vuex is essentially stored in memory, so it has a characteristic that after the browser presses F5 to refresh, the data is gone. So when a page jump occurs, we should distinguish whether the page jump occurs after the user clicks the menu button on the page or whether the page jump occurs after the user clicks the browser refresh button (or presses F5).

In order to achieve this, we need to use the route navigation guard function in vue. For us Java engineers, these may sound a bit unfamiliar, but you can treat it as a Filter in Java. It’s easy to understand. In fact, we used this analogy when explaining it to our friends in the video. It’s easy to understand by comparing a new thing to an already familiar thing in our minds.

The navigation guard in vue is similar to a monitor. It can monitor all page jumps. During page jumps, we can judge whether the menu data in vuex is still there. If it is still there, It means that the user clicked the menu button on the page to complete the jump. If it is not there, it means that the user clicked the browser's refresh button or pressed F5 to refresh the page. At this time, we need to quickly go to the server. Reload the menu data.

---xxxxxxxxxxxxxxxxxxxxx---

The overall implementation idea is like this. Next, let’s take a look at some specific implementation details.

2. Implementation details

2.1 Loading details

First let’s take a look at the loading details.

As you know, the entrance to the single-page project is main.js, and the content loaded by the route is in the src/permission.js file, which is introduced in main.js. The content of the front navigation guard in src/permission.js is as follows:

router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    to.meta.title && useSettingsStore().setTitle(to.meta.title)
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    } else {
      if (useUserStore().roles.length === 0) {
        isRelogin.show = true
        // 判断当前用户是否已拉取完user_info信息
        useUserStore().getInfo().then(() => {
          isRelogin.show = false
          usePermissionStore().generateRoutes().then(accessRoutes => {
            // 根据roles权限生成可访问的路由表
            accessRoutes.forEach(route => {
              if (!isHttp(route.path)) {
                router.addRoute(route) // 动态添加可访问路由表
              }
            })
            next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
          })
        }).catch(err => {
          useUserStore().logOut().then(() => {
            ElMessage.error(err)
            next({ path: '/' })
          })
        })
      } else {
        next()
      }
    }
  } else {
    // 没有token
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单,直接进入
      next()
    } else {
      next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
      NProgress.done()
    }
  }
})

Let me walk you through the ideas in this front navigation guard:

  • First call getToken Method, this method actually gets the authentication token from the cookie, which is the JWT string that the backend returns to the frontend after successful login.

  • If the getToken method has a return value, it means that the user has logged in, then enter the if branch. If getToken does not get the value, it means that the user is not logged in. If not, it is divided into There are two situations: i: the accessed target address is in the login-free whitelist, then you can access it directly; ii: the accessed target address is not in the whitelist, then you will jump to the login page at this time, jump At the same time, it carries a redirect parameter, so that after successful login, it is convenient to jump back to the target page visited. This whitelist for login-free access is a variable defined in the src/permission.js file. There are four default paths, namely ['/login', '/auth-redirect', '/bind' , '/register'].

  • If getToken gets the value, it means that the user has logged in. At this time, there are different situations: if the path the user accesses is the login page, then he will be redirected to the project homepage (also That is, if the user is already logged in, the user is not allowed to access the login page again); if the path the user accesses is not the login page, then first determine whether the roles in vuex still have a value? If there is a value, it means that the user clicked a menu button to jump, so just jump directly; if there is no value, it means that the user pressed the browser's refresh button or the F5 button to refresh the page. Then first call the getInfo method (located in the src/store/modules/user.js file) to reload the current user's basic information, role information and permission information on the server, and then call the generateRoutes method (located in src/store/modules /permission.js file) go to the server to load routing information, and put the loaded routing information into the router object (provided that this routing object is not an http link, but an ordinary routing address).

This is the overall idea of ​​loading dynamic routing.

In the third step, two methods are involved, one is getInfo and the other is generateRoutes. These two methods are also critical. Let’s take a closer look.

2.2 getInfo

First of all, this method of loading user information is located in the src/store/modules/user.js file. In other words, after the basic information of these users is loaded , is stored in vuex. If you refresh the browser, these data will be lost: The logic of the

getInfo() {
  return new Promise((resolve, reject) => {
    getInfo().then(res => {
      const user = res.user
      const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
      if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
        this.roles = res.roles
        this.permissions = res.permissions
      } else {
        this.roles = ['ROLE_DEFAULT']
      }
      this.name = user.userName
      this.avatar = avatar;
      resolve(res)
    }).catch(error => {
      reject(error)
    })
  })
},

method is actually not much to say. Combined with the JSON format returned by the server, it should be easy to understand ( Partial JSON):

{
    "permissions":[
        "*:*:*"
    ],
    "roles":[
        "admin"
    ],
    "user":
        "userName":"admin",
        "nickName":"TienChin健身",
        "avatar":"",
    }
}

另外再强调下,之前在 vhr 中,我们是将请求封装成了一个 api.js 文件,里边有常用的 get、post、put 以及 delete 请求等,然后在需要使用的地方,直接去调用这些方法发送请求即可,但是在 TienChin 中,脚手架的封装是将所有的请求都提前统一封装好,在需要的时候直接调用封装好的方法,连请求地址都不用传递了(封装的时候就已经写死了),所以小伙伴们看上面的 getInfo 方法只有方法调用,没有传递路径参数等。

2.3 generateRoutes

generateRoutes 方法则位于 src/store/modules/permission.js 文件中,这里值得说道的地方就比较多了:

generateRoutes(roles) {
  return new Promise(resolve => {
    // 向后端请求路由数据
    getRouters().then(res => {
      const sdata = JSON.parse(JSON.stringify(res.data))
      const rdata = JSON.parse(JSON.stringify(res.data))
      const defaultData = JSON.parse(JSON.stringify(res.data))
      const sidebarRoutes = filterAsyncRouter(sdata)
      const rewriteRoutes = filterAsyncRouter(rdata, false, true)
      const defaultRoutes = filterAsyncRouter(defaultData)
      const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
      asyncRoutes.forEach(route => { router.addRoute(route) })
      this.setRoutes(rewriteRoutes)
      this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
      this.setDefaultRoutes(sidebarRoutes)
      this.setTopbarRoutes(defaultRoutes)
      resolve(rewriteRoutes)
    })
  })
}

首先大家看到,服务端返回的动态菜单数据解析了三次,分别拿到了三个对象,这三个对象都是将来要用的,只不过使用的场景不同,下面结合页面的显示跟大家细说。

  • 首先是调用 filterAsyncRouter 方法,这个方法的核心作用就是将服务端返回的 component 组件动态加载为一个 component 对象。不过这个方法在调用的过程中,后面还有两个参数,第二个是 lastRouter 在该方法中并无实质性作用;第三个参数则主要是说是否需要对 children 的 path 进行重写。小伙伴们知道,服务端返回的动态菜单的 path 属性都是只有一层的,例如一级菜单系统管理的 path 是 system,二级菜单用户管理的 path 则是 user,那么用户管理最终访问的 path 就是 system/path,如果第三个参数为 true,则会进行 path 的重写,将 path 最终设置正确。

  • 所以这里的 sidebarRoutes 和 defaultRoutes 只是能用于菜单渲染(因为这两个里边的菜单 path 不对),而最终的页面跳转要通过 rewriteRoutes 才可以实现。

  • 除了服务端返回的动态菜单,前端本身也定义了一些基础菜单,前端的基础菜单分为两大类,分别是 constantRoutes 和 dynamicRoutes,其中 constantRoutes 是固定菜单,也就是一些跟用户权限无关的菜单,例如 404 页面、首页等;dynamicRoutes 是动态菜单,也就是也根据用户权限来决定是否展示的菜单,例如分配用户、字典数据、调度日志等等。

  • filterDynamicRoutes 方法则是将前端提前定义好的 dynamicRoutes 菜单进行过滤,找出那些符合当前用户权限的菜单将之添加到路由中(这些菜单都不需要在菜单栏渲染出来)。

  • 接下来涉及到四个不同的保存路由数据的变量,分别是 routes、addRoutes(经松哥分析,这个变量并无实际作用,可以删除之)、defaultRoutes、topbarRouters 以及 sidebarRouters,四个路由变量的作用各有不同:

routes:

routes 中保存的是 constantRoutes 以及服务端返回的动态路由数据,并且这个动态路由数据中的 path 已经完成了重写,所以这个 routes 主要用在两个地方:

首页的搜索上:首页的搜索也可以按照路径去搜索,所以需要用到这个 routes,如下图:

How to load dynamic menu in Vue3

用在 TagsView,这个地方也需要根据页面渲染不同的菜单,也是用的 routes:

How to load dynamic menu in Vue3

sidebarRouters:

这个就是大家所熟知的侧边栏菜单了,具体展示是 constantRoutes+服务端返回的菜单,不过这些 constantRoutes 基本上 hidden 属性都是 false,渲染的时候是不会被渲染出来的。

How to load dynamic menu in Vue3

topbarRouters:

这个是用在 TopNav 组件中,这个是将系统的一级菜单在头部显示出来的,如下图:

How to load dynamic menu in Vue3

一级菜单在顶部显示,左边显示的都是二级三级菜单,那么顶部菜单的渲染,用的就是这个 topbarRouters。

defaultRoutes:

想要开启顶部菜单,需要在 src/layout/components/Settings/index.vue 组件中设置,如下图:

How to load dynamic menu in Vue3

开启顶部菜单之后,点击顶部菜单,左边菜单栏会跟着切换,此时就是从 defaultRoutes 中遍历出相关的菜单设置给 sidebarRouters。

Okay, this is the role of these four routes variables. To be honest, the code design in this part of the scaffolding is a bit confusing. There is no need to have so many variables. Let Brother Song take the time to optimize it for everyone.

ThegenerateRoutes method will eventually return the rewriteRoutes variable to the front navigation guard mentioned above, and finally the front navigation guard will add the data to the router.

The rendering of the menu is done in src/layout/components/Sidebar/index.vue. After looking at it, it is a normal operation. There is nothing to say.

The above is the detailed content of How to load dynamic menu in Vue3. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete