Vue + Element UI front-end (10): dynamic loading menu

Vue + Element UI implements permission management system front-end (10): dynamic loading menu 

Dynamically loading menu

In the past, our navigation tree was hard-coded in the page, but in actual applications, it was dynamically generated after obtaining the menu data from the backend server.

Here we will use the data format prepared in the previous article to mock out the simulation data, and then dynamically generate our navigation menu.

Interface modularization

We have always paid attention to modularization. Previously, the interfaces were concentrated in interface.js. We now renamed it api.js, and transferred all the original login, user, and menu-related interfaces to our new interface module file.

The file structure after modularization is as shown below

After modularization, the module interface is written in the corresponding module interface file, as shown below is the login module

login.js

Copy code

import axios from '../axios'

/* 
 * System login module
 */

// Log in
export const login = data => {
    return axios({
        url: '/login',
        method: 'post',
        data
    })
}

// Sign out
export const logout = () => {
    return axios({
        url: '/logout',
        method: 'get'
    })
}

Copy code

After modularization, the parent module can be imported like this

api.js

Copy code

/* 
 * Interface unified integration module
 */
import * as login from './moudules/login'
import * as user from './moudules/user'
import * as menu from './moudules/menu'


//Export all by default
export default {
    login,
    user,
    menu
}

Copy code

Because we are exporting the parent module here, when calling the specific interface, we also need to add the module on the original basis, like this.

As in api.js above, we exported the entire login file, and there are multiple methods such as login and logout under the login file.

Navigation menu tree interface

We create an interface to query the navigation menu tree under menu.js.

Copy code

import axios from '../axios'

/*
 * Menu management module
 */

export const findMenuTree = () => {
    return axios({
        url: '/menu/findTree',
        method: 'get'
    })
}

Copy code

 If it is not introduced in api.js, remember to introduce it.

Page interface call

The interface is already there. In the navigation menu component MenuBar.vue, we load the menu and store it in the store.

Page menu rendering

Still in MenuBar.vue, the page reads store data through the encapsulated menu tree component and generates menus recursively.

Create a new menu tree component, generate a menu recursively, and jump to the specified route according to the menu URL in the click response function.

components/MenuTree/index.js

Copy code

<template>
  <el-submenu v-if="menu.children && menu.children.length >= 1" :index="menu.menuId + ''">
    <template slot="title">
      <i :class="menu.icon"></i>
      <span slot="title">{
  
  {menu.name}}</span>
    </template>
    <MenuTree v-for="item in menu.children" :key="item.menuId" :menu="item"></MenuTree>
  </el-submenu>
  <el-menu-item v-else :index="menu.menuId + ''" @click="handleRoute(menu)">
    <i :class="menu.icon"></i>
    <span slot="title">{
  
  {menu.name}}</span>
  </el-menu-item>
</template>

<script>
  export default {
    name: 'MenuTree',
    props: {
      menu: {
        type: Object,
        required: true
      }
    },
    methods: {
      handleRoute (menu) {
        // Jump to the specified route through the menu URL
        this.$router.push(menu.url)
      }
    }
  }
</script>

Copy code

Provide Mock data

The interface is in place, and the page calling and rendering have been written. It’s time to provide Mock data.

There is too much data corresponding to the mock findTree interface in mock/modules/menu.js, so I won’t post it here.

Copy code

export function findTree() {
  return {
    url: 'http://localhost:8080/menu/findTree',
    type: 'get',
    data: menuTreeData // json object data
  }
}

Copy code

Test effect

After the startup is completed and we enter the homepage, we see that the navigation menu has been successfully loaded, oh yeah!

However, we happily clicked on the menu and found that this was the case, oh no!

There is nothing wrong with it, but obviously, you are smart and have seen through everything. We only provided a route called /user before, and did not provide a route for /sys/user.

Okay, let's modify it a little, open the routing configuration, and try changing /user to /sys/user.

Sure enough, after modification, you can jump to the user interface normally.

But that’s not right. The routing configuration here is hard-coded. The navigation menu is dynamically generated from the menu data. This routing configuration should also be dynamically added based on the menu data. Well, so next we will discuss the issue of dynamic routing configuration.

Dynamic routing implementation

AddRoutes is provided in vue's route to implement dynamic routing. Open MenuBar.vue, and we add dynamic routing configuration while loading the navigation menu.

MenuBar.vue

Among them, addDynamicMenuRoutes is the key code to return dynamic routing configuration according to the menu.

addDynamicMenuRoutes method details:

Copy code

    /**
     * Add dynamic (menu) routing
     * @param {*} menuList menu list
     * @param {*} routes dynamic (menu) routes created recursively
     */
    addDynamicMenuRoutes (menuList = [], routes = []) {
      var temp = []
      for (var i = 0; i < menuList.length; i++) {
        if (menuList[i].children && menuList[i].children.length >= 1) {
          temp = temp.concat(menuList[i].children)
        } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
          menuList[i].url = menuList[i].url.replace(/^\//, '')
          //Create routing configuration
          var route = {
            path: menuList[i].url,
            component: null,
            name: menuList[i].name,
            meta: {
              menuId: menuList[i].menuId,
              title: menuList[i].name,
              isDynamic: true,
              isTab: true,
              iframeUrl: ''
            }
          }
          // The url starts with http[s]:// and is displayed through iframe
          if (isURL(menuList[i].url)) {
            route['path'] = menuList[i].url
            route['name'] = menuList[i].name
            route['meta']['iframeUrl'] = menuList[i].url
          } else {
            try {
              // Dynamically load the vue component according to the menu URL. It is required that the vue component must be stored according to the url path.
              // If url="sys/user", the component path should be "@/views/sys/user.vue", otherwise the component cannot be loaded.
              let array = menuList[i].url.split('/')
              let url = array[0].substring(0,1).toUpperCase()+array[0].substring(1) + '/' + array[1].substring(0,1).toUpperCase()+array[1]  .substring(1)
              route['component'] = resolve => require([`@/views/${url}`], resolve)
            } catch (e) {}
          }
          routes.push(route)
        }
      }
      if (temp.length >= 1) {
        this.addDynamicMenuRoutes(temp, routes)
      } else {
        console.log(routes)
      }
      return routes
    }

Copy code

The component structure of the dynamic menu page is slightly adjusted. It needs to match the menu URL so that the component path can be determined based on the menu URL to dynamically load the component.

Clean up the routing file, dispose of the routing configuration related to the dynamic menu, and leave some fixed global routes.

Dynamic routing test

After the startup is completed, enter the home page, click User Management, and be routed to the user management page.

 Click Organization Management to be directed to the organization management page.

 

Okay, the dynamic routing function has been implemented here, give yourself a round of applause.

Big hole appears when page refreshes

Previously, we placed the loading of the navigation menu and routing in the menu bar page MenuBar.vue. All display and routing were normal, and there seemed to be no problem. However, when we refresh the page based on the path, the problem occurs.

As shown in the figure below, when we click to refresh the browser on the user management page, it will be completely white. This is because refreshing the browser will cause the entire vue to be reloaded, the routing will be re-initialized, and later in Menu.bar The added dynamic route is gone, so no matching route is found when jumping, and the page that jumps is a page that does not exist, so it is completely blank.

Professional pit filling guide

This is obviously because the loading timing of the dynamic menu and routing is wrong. How to solve this problem? Since the problem lies in the loading timing, just find a place that can trigger reloading when the page is refreshed.

There are many such places, such as the hook function in the Vue loading process, the route navigation guard function, etc. Here we choose to load it in the beforeEach function of the route navigation guard to ensure that every time the route jumps, there will be dynamics. Menus and routing.

Transfer the code that originally loaded the dynamic menu and routing in MenuBar.vue to the routing configuration router/index.

beforeEach:

Copy code

router.beforeEach((to, from, next) => {
  // After successful login on the login interface, the user information will be saved in the session
  //The existence time is the session life cycle, and it will expire when the page is closed.
  let isLogin = sessionStorage.getItem('user')
  if (to.path === '/login') {
    // If you are accessing the login interface, if the user session information exists, it means you have logged in and jump to the home page.
    if(isLogin) {
      next({ path: '/' })
    } else {
      next()
    }
  } else {
    // If you access the non-login interface and the user session information does not exist, it means you are not logged in and jump to the login interface.
    if (!isLogin) {
      next({ path: '/login' })
    } else {
      //Load dynamic menu and routing
      addDynamicMenuAndRoutes()
      next()
    }
  }
})

Copy code

addDynamicMenuAndRoutes:

Copy code

/**
* Load dynamic menus and routing
*/
function addDynamicMenuAndRoutes() {
  api.menu.findMenuTree()
  .then( (res) => {
    store.commit('setMenuTree', res.data)
    //Add dynamic route
    let dynamicRoutes = addDynamicRoutes(res.data)
    router.options.routes[0].children = router.options.routes[0].children.concat(dynamicRoutes)
    router.addRoutes(router.options.routes);
  })
  .catch(function(res) {
    alert(res);
  });
}

Copy code

addDynamicRoutes:

Copy code

/**
* Add dynamic (menu) routing
* @param {*} menuList menu list
* @param {*} routes dynamic (menu) routes created recursively
*/
function addDynamicRoutes (menuList = [], routes = []) {
 var temp = []
 for (var i = 0; i < menuList.length; i++) {
   if (menuList[i].children && menuList[i].children.length >= 1) {
     temp = temp.concat(menuList[i].children)
   } else if (menuList[i].url && /\S/.test(menuList[i].url)) {
     menuList[i].url = menuList[i].url.replace(/^\//, '')
     //Create routing configuration
     var route = {
       path: menuList[i].url,
       component: null,
       name: menuList[i].name,
       meta: {
         menuId: menuList[i].menuId,
         title: menuList[i].name,
         isDynamic: true,
         isTab: true,
         iframeUrl: ''
       }
     }
     // The url starts with http[s]:// and is displayed through iframe
     if (isURL(menuList[i].url)) {
       route['path'] = menuList[i].url
       route['name'] = menuList[i].name
       route['meta']['iframeUrl'] = menuList[i].url
     } else {
       try {
         // Dynamically load the vue component according to the menu URL. It is required that the vue component must be stored according to the url path.
         // If url="sys/user", the component path should be "@/views/sys/user.vue", otherwise the component cannot be loaded.
         let array = menuList[i].url.split('/')
         let url = array[0].substring(0,1).toUpperCase()+array[0].substring(1) + '/' + array[1].substring(0,1).toUpperCase()+array[1]  .substring(1)
         route['component'] = resolve => require([`@/views/${url}`], resolve)
       } catch (e) {}
     }
     routes.push(route)
   }
 }
 if (temp.length >= 1) {
   addDynamicRoutes(temp, routes)
 } else {
   console.log(routes)
 }
 return routes
}

Copy code

Of course, don’t forget to introduce a few things you need to use and clean up the code of the navigation menu bar.

Test effect

After the startup is completed, go to the home page, click User Management, and click the refresh button.

After refreshing, the menu is closed, but the page still stays on the user management page correctly. Mom no longer has to worry about me refreshing!

Save loading status

Now every time the route jumps, the menu data will be re-obtained to generate the menu and route. Even if the page is not refreshed, it will be obtained again, which will affect the performance. Let's improve it by saving the status to the store after successful loading. Check the loading status of the store before each load, so as to avoid repeated loading in the absence of page refresh.

 Add menu routing loading status in the store to avoid repeated loading of the page without refreshing.

Modify the routing configuration, determine the loading status before loading, load only if it is not loaded, and save the loading status after loading.

solve a problem

When routing jumps, the routing seems to be superimposed on the original path and the routing path jumps.

If the path is http://localhost:8090/#/sys/dept, click User Management.

The code corresponds to this.$router.push(‘’sys/user), and the route is earned http://localhost:8090/#/sys/sys/user.

There is one more sys than the correct route. The reason is not yet known.

Currently, I jump back to the main page first and then do the real jump before actually jumping.

This problem can be solved, but it is not good to have an extra jump for no reason. The solution is in progress. . .

Guess you like

Origin blog.csdn.net/y19910825/article/details/132696924