基於iview的router經常使用控制方式

1 iview的router控制需求

最近在使用iview框架寫項目,遇到了一些路由控制上的問題,解決過程當中也有一些心得,故在此記錄下來.
每一個項目在開發時,對於相似tags(標籤頁)的控制需求都不盡相同,故如下先列出本文所述項目對標籤頁的控制要求(若有不一樣需求,本文當也可提供一些思路):vue

  1. 對於同名(name)的路由標籤頁,不能打開多個.譬如說從商品列表中打開商品展現標籤頁,若是已經有在打開的商品編輯頁面,則替換之.新打開的,未保存,已保存的標籤頁,同時只能存在一個(即不一樣params相同name的route只能有一個);
  2. 替換掉一個新的頁面時,經過切換的方式切換回來(先切到其餘標籤頁再切換回來),還是原來頁面的內容(即實際記錄的params在替換後應變化).相似的狀況,還應包含單據從未保存到已保存,以及保存並新增功能;

2 基於vue的router控制

iview是基於vue的框架,故vue自己自帶的router控制方法是必然可行的.
vue變動路由的經常使用方式參考如下(該方法在官方api中有更詳細的介紹):vuex

//變動當前路由(有歷史記錄,建議使用此方式)
this.$router.push({
    name:'routerName',
    params:routerParam
})
//變動當前路由(無歷史記錄)
this.$router.replace({
    name:'routerName',
    routerParam
})

官方路由變動確實能夠正常打開標籤頁,但在實現1中所提到的各類需求的時候,就有些不知足需求了.爲此,須要參考3中,如何基於iview的outer控制.api


3 基於iview的router控制

iview在控制路由的時候,使用vuex中的app.js來記錄標籤頁路由信息,若是對vuex仍是很瞭解的話,能夠經過這篇博文來先打一下基礎.app

3.1 如何實現需求1.1

想要實現不一樣params相同name的route在iview中只能有一個,關鍵是改變iview對路由相等的判斷方法,即'/src/libs/util.js'裏的routeEqual方法:框架

/**
 * @description 根據name/params/query判斷兩個路由對象是否相等
 * @param {*} route1 路由對象
 * @param {*} route2 路由對象
 */
export const routeEqual = (route1, route2) => {
  return route1.name === route2.name
  // 此處改變相同路由的判斷方式,改成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)
}

這裏稍微解釋下(若是不關注緣由,能夠直接看3.2).當改變路由時,'src\components\main\main.vue'做爲近乎頂層的組件控制着近乎全部的全局邏輯,其中就有對路由的監控:iview

...
<side-menu
accordion
ref="sideMenu"
:active-name="$route.name"
:collapsed="collapsed"
@on-select="turnToPage"
:menu-list="menuList"
>
...
    //此方法隸屬於methods,用以監控side-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: {
    // 檢測route的變化
    $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)
    }
},
...

從以上代碼可推測出,main.vue經過turnToPage方法實現打開標籤頁的邏輯,但方法內部並無體現便籤頁顯示效果變化(包含內部數據變化,如下同)的邏輯,這是因爲顯示效果變化的邏輯,由對$router的監控實現.
這樣,不止從左側菜單打開新標籤頁能夠實現顯示變化效果,其餘只要使用vue的原版push等方法改變router的方法,都可監測到.
逐步查看下各個方法,其中影響當前標籤頁顯示效果的,是'src/store/module/app.js'的addTag方法.ide

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])
  }
},

儘管方法內部仍調用了不少,其中一個很重要的判斷,就是routeHasExist(路由是否存在),這個方法也是判斷是否爲相同標籤頁的一個關鍵節點(該方法一樣在util.js):this

/**
 * 判斷打開的標籤列表裏是否已存在這個新添加的路由對象
 */
export const routeHasExist = (tagNavList, routeItem) => {
  let len = tagNavList.length
  let res = false
  doCustomTimes(len, (index) => {
    if (routeEqual(tagNavList[index], routeItem)) res = true
  })
  return res
}

明顯能夠看出,這個方法內調用routeEqual,就是用以判斷是否爲相同路由的實際方法(固然是經過比較新路由與已有路由進行比較),如此,僅需改變routeEqual便可.
以防萬一,全局搜索下調用這個routeEqual的全部方法,發現全部調用的地方再routeEqual在改變後不會出現新的問題..net

3.2 如何實現需求1.2

在進行3.1的操做後,問題獲得了部分解決.餘下的問題在於需求1.2沒有獲得實現和解決.
首先是,如何實現從列表中打開或新建的,替換原來的標籤頁,在來回切換後不會回到原來的標籤頁. 只需在app.js中註冊改變標籤頁參數的方法:code

// 變動指定路由的參數
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])
  }
},

而後在main.vue中對$route的監控最後引用便可.

watch: {
    // 檢測route的變化
    $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)
      // 增長路由參數變動環節
      this.changeTagParams(newRoute)
    }
},

其次,若是出現像保存並新增,或者從未保存到已保存,這兩種狀況來回切換後不會回到原來的狀況.
保存並新增,關鍵是"新增"效果:

// 清空數據,該方法在保存後調用
clearData () {
  //該部分是用來清除當前route的參數
  this.$router.push({
    params: Object.assign(this.$route.params, { id: undefined })
  })
  //這部分代碼是用來清空當前頁面內容,每一個模塊都不盡相同,沒必要模仿
  this.mOtherExpense = JSON.parse(JSON.stringify(this.mOtherExpenseInitial))
  this.tableData = [{}]
  this.loadCode()
  this.mOtherExpense.openingDate = new Date()
},

從未保存到已保存,關鍵一樣是如何讓route記住新的id(或其餘參數):

// 設置路由id,該方法在第一次保存後調用
setData (id) {
  //這裏的id是保存後從後臺傳來的新id
  this.$router.push({
    params: Object.assign(this.$route.params, { id })
  })
}

4 其餘

文中已將本人經常使用的iview router控制方式提出,或有未涉及者,根據如下了解大概也可解決:

  1. app.js中的state.tagNavList是標籤頁中顯示的標籤集合;
  2. 若是要改變一些內容,main.vue中對$route的監控是事件發起的開端,可考慮從這裏修改;
相關文章
相關標籤/搜索