Dynamic Loading of Vue Components in Privilege Management Module

Keywords: Vue JSON Spring github

At present, when the back-end is separated, the handling of the privilege problem is also a little different from our traditional way of dealing with it. The author happened to be in charge of a project's rights management module a few days ago. Now the rights management module has been completed. I want to introduce the problems encountered in the project and my solutions through 5-6 articles. I hope this series can give some help to small partners. This series of articles is not a hands-on tutorial. It mainly introduces the core ideas and explains the core code. Complete code partners can star t and clone on GitHub. In addition, the original plan was to run the project online for small partners to see, but before the purchase of servers to save money, memory only 512M, two applications can not run (there is already one). V Tribe Open Source Project So the little buddies can only take a look at the screenshots below. GitHub has a deployment tutorial, and you can see the full effect locally.

Project address: https://github.com/lenve/vhr

In the previous articles, we have basically solved the problem of server side and encapsulated the front-end requests. In this article, we mainly talk about login and dynamic loading of components.

This is the fifth article in this series. It is suggested that reading the previous article will help us understand this article better.

1.SpringBoot+Vue front-end and back-end separation, using Spring Security to handle permission issues perfectly (1)
2.SpringBoot+Vue front-end and back-end separation, using Spring Security to handle permission issues perfectly (2)
3.Cryptographic Salting in Spring Security and Abnormal Unified Processing in Spring Boot
4.axios request encapsulation and exception unification processing

Logon status saving

When the user logs in successfully, the login information of the current user needs to be saved locally for later use. The concrete realization is as follows:

Logon successfully saves data

After the successful login operation, the data is submitted to the store through the commit operation. The core code is as follows:

this.postRequest('/login', {
    username: this.loginForm.username,
    password: this.loginForm.password
}).then(resp=> {
    if (resp && resp.status == 200) {
    var data = resp.data;
    _this.$store.commit('login', data.msg);
    var path = _this.$route.query.redirect;
    _this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});
    }
});

store

The core code of store is as follows:

export default new Vuex.Store({
  state: {
    user: {
      name: window.localStorage.getItem('user' || '[]') == null ? 'Not logged in' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,
      userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface
    }
  },
  mutations: {
    login(state, user){
      state.user = user;
      window.localStorage.setItem('user', JSON.stringify(user));
    },
    logout(state){
      window.localStorage.removeItem('user');
    }
  }
});

In order to reduce the trouble, the data after successful login will be saved in the local Storage (to prevent data loss after users refresh according to F5), stored in the form of strings, and then transferred to json when retrieved. When the user logs out, the data in the local Storage is cleared.

Dynamic Loading of Components

In the privilege management module, this is the core of the front-end.

Core thinking

After successful login and before entering home homepage, users send requests to the server to obtain the current menu information and component information. The server returns a json string according to the roles of the current user and the corresponding resources of the roles. The format is as follows:

[
    {
        "id": 2,
        "path": "/home",
        "component": "Home",
        "name": "Employee information",
        "iconCls": "fa fa-user-circle-o",
        "children": [
            {
                "id": null,
                "path": "/emp/basic",
                "component": "EmpBasic",
                "name": "Basic data",
                "iconCls": null,
                "children": [],
                "meta": {
                    "keepAlive": false,
                    "requireAuth": true
                }
            },
            {
                "id": null,
                "path": "/emp/adv",
                "component": "EmpAdv",
                "name": "Advanced data",
                "iconCls": null,
                "children": [],
                "meta": {
                    "keepAlive": false,
                    "requireAuth": true
                }
            }
        ],
        "meta": {
            "keepAlive": false,
            "requireAuth": true
        }
    }
]

After the front end gets the string, it does two things: 1. dynamically add json to the current routing; 2. store the data in the store, and then render the menu according to the data in the store.

The core idea is not difficult, let's take a look at the implementation steps.

Data Request Timing

This is very important.

There may be a small partner who says it's difficult. Wouldn't it be okay to request after the login is successful? Yes, after successful login, it is possible to request menu resources. After the request arrives, we save them in the store for the next use, but there is another problem. If the user successfully logs in, clicks on a sub-page, enters the sub-page, and then clicks F5 to refresh, then GG will be available, because after F5 refresh, stores will be in the store. There are two ways to solve this problem: 1. Save the menu resources not in the store, but in the local Storage, so that the data is still there even after F5 refresh; 2. Load the menu resources directly in the mounted method of each page.

Because menu resources are very sensitive, it's better not to save them locally, so abandon scheme 1, but the workload of scheme 2 is a bit heavy, so I take a way to simplify it, the way is to use the navigation guard in the route.

Route Navigation Guard

My implementation is as follows: first, create an array of routes in the store, which is an empty array, and then turn on the router global guard, as follows:

router.beforeEach((to, from, next)=> {
    if (to.name == 'Login') {
      next();
      return;
    }
    var name = store.state.user.name;
    if (name == 'Not logged in') {
      if (to.meta.requireAuth || to.name == null) {
        next({path: '/', query: {redirect: to.path}})
      } else {
        next();
      }
    } else {
      initMenu(router, store);
      next();
    }
  }
)

The code here is very short. Let me give you a brief explanation.
1. If the page you want to go to is the login page, there's nothing to say about it, just go through it.

2. If it is not a login page, I first get the current login status from the store. If it is not logged in, I use the requireAuth attribute of the meta attribute in the route to determine whether the page to be logged in or not. If it needs to be logged in, I jump back to the login page. At the same time, the path of the page to be logged in is passed to the login page as a parameter to jump to the target page after successful login. If you don't need to log in, go directly (in fact, only the Login page in this project does not need to log in); if you have logged in, initialize the menu first, then jump.

The operation of the initialization menu is as follows:

export const initMenu = (router, store)=> {
  if (store.state.routes.length > 0) {
    return;
  }
  getRequest("/config/sysmenu").then(resp=> {
    if (resp && resp.status == 200) {
      var fmtRoutes = formatRoutes(resp.data);
      router.addRoutes(fmtRoutes);
      store.commit('initMenu', fmtRoutes);
    }
  })
}
export const formatRoutes = (routes)=> {
  let fmRoutes = [];
  routes.forEach(router=> {
    let {
      path,
      component,
      name,
      meta,
      iconCls,
      children
    } = router;
    if (children && children instanceof Array) {
      children = formatRoutes(children);
    }
    let fmRouter = {
      path: path,
      component(resolve){
        if (component.startsWith("Home")) {
          require(['../components/' + component + '.vue'], resolve)
        } else if (component.startsWith("Emp")) {
          require(['../components/emp/' + component + '.vue'], resolve)
        } else if (component.startsWith("Per")) {
          require(['../components/personnel/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sal")) {
          require(['../components/salary/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sta")) {
          require(['../components/statistics/' + component + '.vue'], resolve)
        } else if (component.startsWith("Sys")) {
          require(['../components/system/' + component + '.vue'], resolve)
        }
      },
      name: name,
      iconCls: iconCls,
      meta: meta,
      children: children
    };
    fmRoutes.push(fmRouter);
  })
  return fmRoutes;
}

In the initialization menu, the first step is to determine whether the data in the store exists. If it exists, it means that the jump is a normal jump, rather than the user entering an address by F5 or directly in the address bar. Otherwise, load the menu. After getting the menu, we first change the json returned by the server to the format required by the router through formatRoutes method. This is mainly to turn component, because the component returned by the server is a string, while the component needed by the router is a component, so we can dynamically load the required component in the formatRoutes method. Once the data format is ready successfully, on the one hand, the data is stored in the store, on the other hand, it is dynamically added to the routing by using the addRoutes method in the routing.

Menu rendering

Finally, in the Home page, you can get the menu json from the store and render it into a menu. The relevant code can be viewed in Home.vue, without further elaboration.

OK, so that different users can see different menus after successful login.

Pay attention to the public number and receive the latest articles in time:

Posted by dnzone on Sat, 18 May 2019 19:04:07 -0700