iview-based routing control

router Control Requirements for 1 iview

Recently, when writing projects using iview framework, we encountered some problems in routing control and some experiences in solving them, so we record them here.
Each project has different control requirements for tags (tabs). Therefore, the control requirements for tags (tabs) of the projects described in this paper are listed below (if there are different requirements, this paper can also provide some ideas):

  1. For routing tabs with the same name, you can't open more than one. For example, if you open a commodity display tab from a commodity list, replace it if you already have an open commodity editing page. Newly opened, unsaved, saved label pages can only exist one (i.e., only one route with the same name for different params);
  2. When a new page is replaced, the content of the original page (i.e. the actual recorded params are changed after replacing) is still changed by switching back (first cutting to other tabs and then switching back). Similarly, it should also include the functions of never saving documents to saved and saving and adding new functions.

router control based on vue

iview is based on the framework of vue, so the router control method of Vue itself is inevitable and feasible.
The common ways in which vue changes routing are referred to below (this method is described in more detail in the official api):

//Change the current routing (historically recommended)
this.$router.push({
    name:'routerName',
    params:routerParam
})
//Change current routing (no history)
this.$router.replace({
    name:'routerName',
    routerParam
})

Official routing changes do open the tab normally, but when implementing the various requirements mentioned in 1, there are some unsatisfactory requirements. For this reason, we need to refer to 3, how to control the outer based on iview.

iview-based router control

iview uses app.js in vuex to record label routing information when controlling routing. If you know vuex well, you can use app.js in vuex to record label routing information. This blog post Let's lay the groundwork first.

3.1 How to Realize Requirement 1.1

There can only be one route with the same name for different params in iview. The key is to change the way iView judges route equality, that is, the routeEqual method in'/src/libs/util.js':

/**
 * @description Determine whether two routing objects are equal according to name/params/query
 * @param {*} route1 Routing object
 * @param {*} route2 Routing object
 */
export const routeEqual = (route1, route2) => {
  return route1.name === route2.name
  // Change the way of judging the same route to the same name.
  // const params1 = route1.params || {}
  // const params2 = route2.params || {}
  // const query1 = route1.query || {}
  // const query2 = route2.query || {}
  // return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2)
}

Here's a slight explanation (if you don't pay attention to the cause, you can look directly at 3.2). When changing routing,'src components main main. vue'controls almost all global logic as a near-top component, including routing monitoring:

...
<side-menu
accordion
ref="sideMenu"
:active-name="$route.name"
:collapsed="collapsed"
@on-select="turnToPage"
:menu-list="menuList"
>
...
    //This method belongs to methods to monitor side-menu selection events, i.e. the logic of opening tabs from the left menu
    turnToPage (route) {
      let { name, params, query } = {}
      if (typeof route === 'string') name = route
      else {
        name = route.name
        params = route.params
        query = route.query
      }
      if (name.indexOf('isTurnByHref_') > -1) {
        window.open(name.split('_')[1])
        return
      }
      this.$router.push({
        name,
        params,
        query
      })
    },
...
watch: {
    // Detecting route Changes
    $route (newRoute) {
      const { name, query, params, meta } = newRoute
      this.addTag({
        route: { name, query, params, meta },
        type: 'push'
      })
      this.setBreadCrumb(newRoute)
      this.setTagNavList(getNewTagList(this.tagNavList, newRoute))
      this.$refs.sideMenu.updateOpenName(newRoute.name)
    }
},
...

It can be inferred from the above code that main.vue implements the logic of opening the tab by turnToPage method, but the logic of changing the display effect of the tab (including internal data changes, the same below) is not reflected inside the method. This is due to the logic of changing the display effect, which is realized by monitoring the $router.
In this way, not only can the new tab be opened from the left menu to display the change effect, but other methods can be monitored as long as the original push method of vue is used to change the router.
Step by step, look at each method. The addTag method of'src/store/module/app.js'affects the display effect of the current tab.

addTag (state, { route, type = 'unshift' }) {
  let router = getRouteTitleHandled(route)
  if (!routeHasExist(state.tagNavList, router)) {
    if (type === 'push') state.tagNavList.push(router)
    else {
      if (router.name === homeName) state.tagNavList.unshift(router)
      else state.tagNavList.splice(1, 0, router)
    }

    setTagNavListInLocalstorage([...state.tagNavList])
  }
},

Although there are still many calls within the method, one of the most important judgments is routeHasExist, which is also a key node for judging whether it is the same tab (the method is also in util.js):

/**
 * Determine whether the newly added routing object already exists in the open tag list
 */
export const routeHasExist = (tagNavList, routeItem) => {
  let len = tagNavList.length
  let res = false
  doCustomTimes(len, (index) => {
    if (routeEqual(tagNavList[index], routeItem)) res = true
  })
  return res
}

Obviously, calling routeEqual in this method is the actual method used to judge whether the same route is the same (by comparing the new route with the existing route, of course), so it is only necessary to change the routeEqual.
Just in case, a global search calls all the methods of the routeEqual and finds that the routeEqual will not have any new problems when it is changed.

3.2 How to Realize Requirement 1.2

After the operation of 3.1, the problem has been partially solved. The remaining problem is that requirement 1.2 has not been realized and solved.
First, how to open or create a new tab from the list, replace the original tab, and do not return to the original tab after switching back and forth. Just register the method of changing the tab parameters in app.js:

// Changing the parameters of the specified routing
changeTagParams (state, route) {
  let routeOldIndex = state.tagNavList.findIndex(m => routeEqual(m, route))
  if (routeOldIndex !== -1) {
    let routeOld = state.tagNavList[routeOldIndex]
    routeOld.params = route.params
    state.tagNavList.splice(routeOldIndex, 1, routeOld)
    setTagNavListInLocalstorage([...state.tagNavList])
  }
},

Then the monitoring of $route in main.vue can be referenced at last.

watch: {
    // Detecting route Changes
    $route (newRoute) {
      const { name, query, params, meta } = newRoute
      this.addTag({
        route: { name, query, params, meta },
        type: 'push'
      })
      this.setBreadCrumb(newRoute)
      this.setTagNavList(getNewTagList(this.tagNavList, newRoute))
      this.$refs.sideMenu.updateOpenName(newRoute.name)
      // Adding Routing Parameter Change Link
      this.changeTagParams(newRoute)
    }
},

Secondly, if something like save and add, or never save to save, the two cases will not return to the original situation after switching back and forth.
The key to preserving and adding is the "new" effect:

// Empty the data, which is called after saving
clearData () {
  //This section is used to clear the parameters of the current route
  this.$router.push({
    params: Object.assign(this.$route.params, { id: undefined })
  })
  //This part of the code is used to empty the current page content, each module is different, do not need to imitate.
  this.mOtherExpense = JSON.parse(JSON.stringify(this.mOtherExpenseInitial))
  this.tableData = [{}]
  this.loadCode()
  this.mOtherExpense.openingDate = new Date()
},

Never saved to saved, the key is also how to let route remember the new ID (or other parameters):

// Set the routing id, which is called after the first save
setData (id) {
  //The id here is the new id that comes in from the background after being saved.
  this.$router.push({
    params: Object.assign(this.$route.params, { id })
  })
}

4 other

In this paper, I have put forward my usual iview router control mode, or not involved, according to the following understanding may also be solved:

  1. The state.tagNavList in app.js is the tag set displayed on the tag page.
  2. If you want to change something, the monitoring of $route in main.vue is the beginning of event initiation, you can consider modifying it from here.

Keywords: Front-end Vue JSON

Added by chugger93 on Wed, 29 May 2019 14:56:22 +0300