最近在使用iview框架写项目,遇到了一些路由控制上的问题,解决过程当中也有一些心得,故在此记录下来.
每一个项目在开发时,对于相似tags(标签页)的控制需求都不尽相同,故如下先列出本文所述项目对标签页的控制要求(若有不一样需求,本文当也可提供一些思路):vue
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
iview在控制路由的时候,使用vuex中的app.js来记录标签页路由信息,若是对vuex仍是很了解的话,能够经过这篇博文来先打一下基础.app
想要实现不一样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.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 }) }) }
文中已将本人经常使用的iview router控制方式提出,或有未涉及者,根据如下了解大概也可解决: