About the routing of the background management system, want to spend a little time, thoroughly organize a dynamic routing dribs and drabs.

First of all, this article is based on the flower Pants god “hand touch hand, take you with vUE masturbating background”, on the basis of his project, to help want to achieve dynamic routing partners, to write a use of notes.

Segmentfault.com/a/119000000…

Why implement dynamic routing?

We are in the process of developing background management system, there will be different people to operate the system, there are admin (administrator), superAdmin(supertube), there will be all kinds of operation personnel, financial personnel. In order to distinguish these people, we assign different roles to different people to display different menus, which must be achieved through dynamic routing.

Mainstream implementation:

After all, if you’ve never done it before, it’s hard to understand. You still have to look at the code

The front-end control

1. The routing table is maintained in the front end without the help of the back-end. 2Copy the code

2. Back-end control

2. The routing table is maintained in the databaseCopy the code

First, front-end control

His solution is front-end control. His core is to control the loading of routes through the meta attribute of routes and role. Specific implementation scheme:

1. Return to the role of the front-end user based on the user's account. 2Copy the code

Specific code logic:

Create a new route guard function, either in main.js or in a separate file. 4. The sidebar can extract data from vuex for renderingCopy the code

Core code altogether four files, basically will add source code and explanation:

1. Write static routes and dynamic routes to router.js separately.

import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) import Layout from '@/layout' // constantRoutes Export const constantRoutes = [{path: '/redirect', Component: Layout, hidden: true, children: [ { path: '/redirect/:path*', component: () => import('@/views/redirect/index') } ] }, { path: '/login', component: () => import('@/views/login/index'), hidden: true }, { path: '/404', component: () => import('@/views/error-page/404'), hidden: true }, { path: '/401', component: () => import('@/views/error-page/401'), hidden: true}] // asyncRoutes export const asyncRoutes = [{path: '/permission', component: Layout, redirect: '/permission/page', alwaysShow: true, name: 'Permission', meta: { title: 'Permission', icon: 'lock', // Core code that allows traversal of roles, allowing access to roles admin and Editor. This menu allows access to roles admin and Editor: ['admin', 'editor'] }, children: [ { path: 'page', component: () => import('@/views/permission/page'), name: 'PagePermission', meta: {title: 'PagePermission', // ['admin'] } } ] } ] const createRouter = () => new Router({ scrollBehavior: () => ({ y: 0 }), routes: ConstantRoutes}) const router = createRouter() Export function resetRouter() {const newRouter = createRouter() router.matcher = newRouter. Matcher} export function resetRouter() {const newRouter = createRouter() router.matcher = newRouter default routerCopy the code

2, Store /permission.js(maintain a state in vuex, control menu display by role)

Import {asyncRoutes, constantRoutes} from '@/router' // function hasPermission(roles, route) { if (route.meta && route.meta.roles) { return roles.some(role => route.meta.roles.includes(role)) } else { Return true}} // This method iterates through the route recursively, Export function filterAsyncRoutes(routes, roles) { const res = [] routes.forEach(route => { const tmp = { ... route } if (hasPermission(roles, tmp)) { if (tmp.children) { tmp.children = filterAsyncRoutes(tmp.children, roles) } res.push(tmp) } }) return res } const state = { routes: [], addRoutes: [] } const mutations = { SET_ROUTES: (state, routes) => {// AddRoutes = routes state. Routes = constantRoutes. Concat (routes)}} const actions = {generateRoutes({routes)} commit }, roles) { return new Promise(resolve => { let accessedRoutes if (roles.includes('admin')) { accessedRoutes = asyncRoutes | | []} else {/ / core code, AccessedRoutes = filterAsyncRoutes(asyncRoutes, Roles)} // commit('SET_ROUTES', accessedRoutes) resolve(accessedRoutes) }) } } export default { namespaced: true, state, mutations, actions }Copy the code

Create a new route guard function, either in main.js or by pulling out a file.

The code above is mainly to control the route jump before checking which routes are accessible, login after the jump logic can be written in this place

// permission.js router.beforeEach((to, from, If (to.path === '/login') {next({path: '/'}); next({path: '/'}); } else {/ / whether the current user has finished pull user_info information if (store) getters) roles. The length = = = 0) {store. Dispatch (' GetInfo). Then (res = > {/ / Info const roles = res.data.role; GenerateRoutes ('GenerateRoutes', {roles}).then(() => { Router. AddRoutes (store.getters. AddRouters) router. AddRoutes (store.getters. to, replace: true }) }) }).catch(err => { console.log(err); }); } else {if (whitelist.indexof (to.path)!} else {if (whitelist.indexof (to.path)! == -1) {// Next (); } else { next('/login'); // Otherwise all redirects to the login page}}})Copy the code

4. The sidebar can take data from vuex for rendering

The core code takes the route object available from the Router and renders the sidebar the same whether the route is dynamically loaded from the front or from the back

layout/components/siderbar/index.vue
<el-menu :default-active="activeMenu" :collapse="isCollapse" :background-color="variables.menuBg" :text-color="variables.menuText" :unique-opened="false" :active-text-color="variables.menuActiveText" <sidebar-item v-for="route in routes" :collapse-transition="false" mode="vertical" > Path :key="route.path" :item="route" :base-path="route.path" /> </el-menu> // Obtain routes with permission () {return this.$router.options.routes }Copy the code
layout/components/siderbar/siderbarItem.vue
  <template slot="title">
    <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
  </template>
  <sidebar-item
    v-for="child in item.children"
    :key="child.path"
    :is-nest="true"
    :item="child"
    :base-path="resolvePath(child.path)"
    class="nest-menu"
  />

  props: {
    // route object
    item: {
      type: Object,
      required: true
    },
    isNest: {
      type: Boolean,
      default: false
    },
    basePath: {
      type: String,
      default: ''
    }
  }
Copy the code

The front end controls the routing with relatively simple logic. The back end only needs to store the user’s role, and the front end matches the user’s role. But if you add new characters, it’s a pain to add each one.

2. Back-end control routing

Back-end control routing is the solution of most back-end management systems, and our company also manages routing in this way. The idea is as follows:

1. After a user logs in, the back-end generates accessible routing data based on the user's role. 2Copy the code

Specific code logic:

1, the router. Js only inside put some static routing, such as login, 404 2, sorting, a data structure to table 3, from the back end for routing data, write a data transformation methods, data into accessible route 4, is also a vuex maintenance state, 5. The sidebar also takes data from the route for renderingCopy the code

Since the front and back controls are mostly the same, this is just a look at the previous different flows:

Store/Permission. js, send a request to get data in vuex

GenerateRoutes({ commit }, data) { return new Promise((resolve, Reject) => {getRoute(data). Then (res => {reject) Const accessedRouters = arrayToMenu(res.data) accessedrouters. concat([{path: '*', redirect: '/404', hidden: true }]) commit('SET_ROUTERS', accessedRouters) resolve() }).catch(error => { reject(error) }) }) }Copy the code

2. Organize a data structure and store it in a table

We know that the data structure specified by vue’s router looks like this:

{
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: 'Form', icon: 'form' }
      }
    ]
}
Copy the code

Therefore, the first level menu must have several parameters: Id, path, name, component, title, children is an array of children and parent relationships, so you can add fid or parentId to match.

{id: 1300 parentId: 0 title: "enterprise management" path: "/enterprise" hidden: false component: null hidden: false name: "Enterprise"}, / / / / parentId secondary menu is not zero, can take parentId with first-level menu id to match, will push on the match to the children in {id: 1307 parentId: 1300 title: "MerchantInfo" hidden: false path: "merchantInfo" component: "enterprise/merchantInfo" // match local file address hidden: false name: "merchantInfo" }Copy the code

Write a conversion method to convert the obtained data into a Router structure

ArrayToMenu (arrayToMenu) {arrayToMenu (arrayToMenu) {arrayToMenu (arrayToMenu);

Export function arrayToMenu(array) {const nodes = [] for (let I = 0; i < array.length; I ++) {const row = array[I] // This exists to determine if (! Exists (array, row.parentid)) {nodes.push({path: row.path, // routing address hidden: row.hidden, // all cphone true if there is no component on the backend: Component name: row.name, // Route name meta: {title: row.title, icon: row.name}, // title is the display name id: Row. id, // Redirect: 'noredirect'})}} const toDo = array. from(nodes) while (todo.length) {const node = todo.shift () // Get child node for (let I = 0; i < array.length; If (row.parentId === node.id) {const child = {path: const row = array[I] // parentId === node.id Path, name: row.name, hidden: row.hidden, // Core code, because the component of secondary routing is the component that needs to match the page: require('@/views/' + row.component + '/index.vue'), meta: { title: row.title, icon: row.name }, id: row.id } if (node.children) { node.children.push(child) } else { node.children = [child] } toDo.push(child) } } } return Function exists(rows, parentId) {for (let I = 0; i < rows.length; i++) { if (rows[i].id === parentId) return true } return false }Copy the code

The code in the sidebar is the same as static code, so I won’t go over it again

conclusion

Is dynamic routing controlled by the front end or the back end? Can only say that each has its own advantages, after all, the business scene is not the same, we can start to try. The code still has a lot of places worth optimizing, welcome everybody big god criticism.

Personal public account: Little Jerry has something to say

Welcome to follow my personal public account to discuss interesting front-end knowledge.