What is Dynamic Routing

Dynamic routing is different from static routing and can change the site routing list according to different “factors”. Common dynamic routing is mostly used to achieve: multi-user permission system different users display different navigation menus.

How to use Vue Router to implement dynamic routing

The Vue project implements dynamic routing in two ways:

  1. The front end defines all routes and dynamically displays routes according to user role permissions during login.
  2. Routes are stored in the database, and the front end obtains the route list corresponding to the current user through the interface and renders them.

The first way is implemented on many Vue UI Admin, you can read their source code to understand the specific implementation ideas, here will not expand more. The second approach is now more common because it was used in a recent project so I will mention it separately. The solution I used here is to use some features of Vue Router to implement back-end led dynamic routing.

Functions and features used

Vue Router global front guard

Website to explain

We use the “front-loading” feature of the global front-guard to inject the list of routes used by the current user into the Router instance before the page loads, using the router.addroutes method below.

Vue Router Router. addRoutes instance method

Website to explain

The router.addRoutes method dynamically adds routing rules to router instances, providing an injection method for dynamic routing.

Vue Router Lazy route loading

Website to explain

Lazy loading is not a necessary feature for dynamic routing, but since it is provided, it is used directly in the project.

Specific ideas

Basic Information

Front-end code to implement basic static routing, such as: login page routing, server error page routing, etc. (there is a pit, will be explained later). The database stores all dynamic routing information.

How does a database store dynamic routing information?

The scheme I choose is to string the object referenced by the route, and then convert the route list into JSON format and transmit it to the back end. After processing by the back end, the route list is stored in the database. In short, the route list information in JSON format is passed on the front and back ends.

How do I string an object referenced in a route?

The real problem I ran into was that the UI component I was using provided a layout scheme that needed to reference the layout component and refer to specific pages at child routes.

My solution is to discriminate between referring to the Component properties of layout components, using short strings instead of layout components, and using file path strings instead of page imports.

See the code example below for details.

The global front guard is used to determine the routing information

1- Check whether the user is logged in. 1.1- If not, go to the login page. 1.2- If yes, check whether the route list is obtainedCopy the code

I did not make too much investigation here, but directly stored the obtained data in Vuex. The security of data storage should be considered in the actual project application process.

How to implement route list resolution?

  1. willJSONThe route information is parsed asJavaScriptList object;
  2. Using list objectsfilterMethod to implement the analytic function, throughcomponentDetermine whether it is a layout component;
  3. If it is a layout component, use a layout component insteadcomponentThe string;
  4. For a specific page, useloadViewThe function loads the corresponding specific page;

The router. AddRoutes method is used to dynamically addRoutes

Add the parsed route list to the Router instance using the router.addRoutes method.

Simple implementation code

// router/index.js
import Vue from 'vue'
import store from '@/store'
import Router from 'vue-router'
import { getToken } from '@/lib/util'

Vue.use(Router)

// Define a static route
const staticRoutes = [
  {
    path: '/login'.name: 'login'.meta: {
      title: 'Login page'.hideInMenu: true
    },
    component: () = > import('@/view/login/login.vue')}, {path: '/ 401'.name: 'error_401'.meta: {
      hideInMenu: true
    },
    component: () = > import('@/view/error-page/401.vue')}, {path: '/ 500'.name: 'error_500'.meta: {
      hideInMenu: true
    },
    component: () = > import('@/view/error-page/500.vue')}]// Define the login page name (defined for ease of understanding)
const LOGIN_PAGE_NAME = 'login'

// Instantiate the Router object
const router = new Router({
  routes: staticRoutes,
  mode: 'history'
})

// Define the global front guard (there are two pits in it)
router.beforeEach((to, from, next) = > {
  // Obtain the user token through a user-defined method to determine the user login status
  const token = getToken()
  if(! token && to.name ! == LOGIN_PAGE_NAME) {// If you are not logged in and you are not going to the login page, go to the login page
    next({ name: LOGIN_PAGE_NAME })
  } else if(! token && to.name === LOGIN_PAGE_NAME) {// If you are not logged in and you are going to the login page, go to the login page
    // There is a pit here, be sure to write this step separately from the previous step
    // If (! token) next({ name:login })
    // Will cause the login page unlimited refresh error, the specific cause is explained later
    next()
  } else {
    // If logged in
    if(! store.state.app.hasGetRoute) {// If no route information is obtained, obtain the route information first and then jump
      store.dispatch('getRouteList').then(() = > {
        router.addRoutes(store.state.app.routeList)
        // This is also a pit, you can't use simple next()
        // If you refresh directly with next(), the screen will remain blanknext({ ... to,replace: true})})}else {
      // If the route information has been obtained, the route is directly forwarded
      next()
    }
  }
})

export default router
Copy the code
// store/index.js
import router from '@/router'
import Main from '@/components/main'
import { getToken } from '@/lib/util'
import { getRoute } from '@/api/app'

const loadView = (viewPath) = > {
  // Implement dynamic import with string template to implement lazy route loading
  return () = > import(`@/view/${viewPath}`)}const filterAsyncRouter = (routeList) = > {
  return routeList.filter((route) = > {
    if (route.component) {
      if (route.component === 'Main') {
        // If component = Main it is a layout component
        // Assign the real layout component to it
        route.component = Main
      } else {
        // If it is not a layout component, it can only be a reference to the page
        // Assign the actual page to it using the lazy load function
        route.component = loadView(route.component)
      }
      // Determine if there are child routes and recursively call yourself
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children)
      }
      return true}})}export default {
  state: {
    routeList: [].token: getToken(),
    hasGetRoute: false
  },
  mutations: {
    setRouteList(state, data) {
      // Parse the JSON route List into a JavaScript List
      // Use the route parsing function to parse the List into a real route List
      state.routeList = filterAsyncRouter(JSON.parse(data))
      // Change the route obtaining status
      state.hasGetRoute = true}},atcions: {
    getRouteList({ state, commit }) {
      return new Promise((resolve) = > {
        const token = state.token
        getRoute({ token }).then((res) = > {
          let data = res.data.data
          // Note that the route list is in JSON format
          commit('setRouteList', data)
          resolve()
        })
      })
    }
  }
}
Copy the code

Q&A

The page gets stuck on the login page and keeps refreshing

The solution to this problem is described in the implementation code, and it’s just a matter of being careful not to confuse the two login states when determining login states. But this is not a cure, because the same problem can be caused by different forms of code, so what causes the problem? But let’s analyze it slowly:

Let’s assume we accidentally mix two states that are not logged in:

if(! token) { next({name: LOGIN_PAGE_NAME })
}
Copy the code

The next({name: LOGIN_PAGE_NAME}) method here activates the global front-guard again, causing another judgment to enter and trigger next({name: LOGIN_PAGE_NAME}), so recursively the page freezes and refreshes.

Dynamic routing and lazy route loading

A solution to this is also shown in the code example:

const loadView = (viewPath) = > {
  return () = > import(`@/view/${viewPath}`)}Copy the code

Here we use a less commonly used JavaScript feature: string templates, which allow import operations that do not support string concatenation to dynamically import different modules.

404 After the dynamic route is refreshed

This is one of the most common errors in this solution. Many people create a “basic static route” and add the 404 page route to it. As a result, the 404 page with the widest matching range will be displayed before the dynamic route is added to the route instance. The solution is to add the 404 page route to the dynamic route.

The dynamic route page becomes blank after being refreshed

There are many reasons for this problem. The problem I met here was solved by referring to article 3, but I haven’t figured out the specific principle yet. I will update it after I do some research.

The Title is unstable when the dynamic routing page is refreshed

The reason for this problem is simple: there is no header information to load because the routing information has not been loaded when the page is refreshed. But I haven’t found a good solution yet, so I’ll update it after I study it.

reference

  1. The realization of Vue dynamic routing……
  2. Vue Router documentation page
  3. Rambo: The page of vue router becomes blank after the dynamic route is refreshed