基于iview的router常用控制方式


Posted in Javascript onMay 30, 2019

1 iview的router控制需求

最近在使用iview框架写项目,遇到了一些路由控制上的问题,解决过程中也有一些心得,故在此记录下来.
每个项目在开发时,对于类似tags(标签页)的控制需求都不尽相同,故以下先列出本文所述项目对标签页的控制要求(如有不同需求,本文当也可提供一些思路):

  1. 对于同名(name)的路由标签页,不能打开多个.譬如说从商品列表中打开商品展示标签页,如果已经有在打开的商品编辑页面,则替换之.新打开的,未保存,已保存的标签页,同时只能存在一个(即不同params相同name的route只能有一个);
  2. 替换掉一个新的页面时,通过切换的方式切换回来(先切到其他标签页再切换回来),仍是原来页面的内容(即实际记录的params在替换后应变化).类似的情况,还应包含单据从未保存到已保存,以及保存并新增功能;

2 基于vue的router控制

iview是基于vue的框架,故vue本身自带的router控制方法是必然可行的.

vue变更路由的常用方式参考以下(该方法在官方api中有更详细的介绍):

//变更当前路由(有历史记录,建议使用此方式)
this.$router.push({
  name:'routerName',
  params:routerParam
})
//变更当前路由(无历史记录)
this.$router.replace({
  name:'routerName',
  routerParam
})

官方路由变更确实可以正常打开标签页,但在实现1中所提到的各种需求的时候,就有些不满足需求了.为此,需要参考3中,如何基于iview的outer控制.

3 基于iview的router控制

iview在控制路由的时候,使用vuex中的app.js来记录标签页路由信息,如果对vuex还是很了解的话,可以通过这篇博文来先打一下基础.

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'作为近乎顶层的组件控制着近乎所有的全局逻辑,其中就有对路由的监控:

...
<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方法.

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):

/**
 * 判断打开的标签列表里是否已存在这个新添加的路由对象
 */
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在改变后不会出现新的问题.

3.2 如何实现需求1.2

在进行3.1的操作后,问题得到了部分解决.余下的问题在于需求1.2没有得到实现和解决.

首先是,如何实现从列表中打开或新建的,替换原来的标签页,在来回切换后不会回到原来的标签页. 只需在app.js中注册改变标签页参数的方法:

// 变更指定路由的参数
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控制方式提出,或有未涉及者,根据以下了解大概也可解决:

app.js中的state.tagNavList是标签页中显示的标签集合;

如果要改变一些内容,main.vue中对$route的监控是事件发起的开端,可考虑从这里修改;

Javascript 相关文章推荐
JQuery的AJAX实现文件下载的小例子
May 15 Javascript
使用GruntJS链接与压缩多个JavaScript文件过程详解
Aug 02 Javascript
jQuery动画效果-slideUp slideDown上下滑动示例代码
Aug 28 Javascript
javascript实现uploadify上传格式以及个数限制
Nov 23 Javascript
快速掌握Node.js模块封装及使用
Mar 21 Javascript
实用又漂亮的BootstrapValidator表单验证插件
May 30 Javascript
jQuery+css实现非常漂亮的水平导航菜单效果
Jul 27 Javascript
微信小程序中多个页面传参通信的学习与实践
May 05 Javascript
Javascript实现从小到大的数组转换成二叉搜索树
Jun 13 Javascript
vue2.0实现的tab标签切换效果(内容可自定义)示例
Feb 11 Javascript
vue项目中全局引入1个.scss文件的问题解决
Aug 01 Javascript
vuex存储复杂参数(如对象数组等)刷新数据丢失的解决方法
Nov 05 Javascript
深入了解js原型模式
May 30 #Javascript
js逆向解密之网络爬虫
May 30 #Javascript
Vue.js中的组件系统
May 30 #Javascript
Vue+Django项目部署详解
May 30 #Javascript
了解重排与重绘
May 29 #Javascript
小程序如何构建骨架屏
May 29 #Javascript
新手简单了解vue
May 29 #Javascript
You might like
php后退一页表单内容保存实现方法
2012/06/17 PHP
php设计模式之命令模式使用示例
2014/03/02 PHP
js 禁用浏览器的后退功能的简单方法
2008/12/10 Javascript
javascript 拖放效果实现代码
2010/01/22 Javascript
JavaScript中将一个值转换为字符串的方法分析[译]
2012/09/21 Javascript
$.getJSON在IE下失效的原因分析及解决方法
2013/06/16 Javascript
jQuery页面加载初始化常用的三种方法
2014/06/04 Javascript
JavaScript极简入门教程(三):数组
2014/10/25 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
每天一篇javascript学习小结(面向对象编程)
2015/11/20 Javascript
原生JS实现垂直手风琴效果
2017/02/19 Javascript
详解Vue2.0配置mint-ui踩过的那些坑
2018/04/23 Javascript
[03:11]TI9战队档案 - Alliance
2019/08/20 DOTA
在Python程序中进行文件读取和写入操作的教程
2015/04/28 Python
玩转python selenium鼠标键盘操作(ActionChains)
2020/04/12 Python
详解python之简单主机批量管理工具
2017/01/27 Python
python如何发布自已pip项目的方法步骤
2018/10/09 Python
Python subprocess库的使用详解
2018/10/26 Python
Python线上环境使用日志的及配置文件
2019/07/28 Python
Python单链表原理与实现方法详解
2020/02/22 Python
Python序列化pickle模块使用详解
2020/03/05 Python
自定义Django_rest_framework_jwt登陆错误返回的解决
2020/10/18 Python
加拿大最大的五金、家居装修和园艺产品商店:RONA
2017/01/27 全球购物
Nike香港官网:Nike HK
2019/03/23 全球购物
学生个人求职自荐信格式
2013/09/23 职场文书
电气专业应届生求职信
2013/11/01 职场文书
写好自荐信的要点
2013/11/06 职场文书
怎样拟定创业计划书
2014/05/01 职场文书
异地年检委托书范本
2014/09/24 职场文书
小学假期安全广播稿
2014/09/28 职场文书
财务出纳岗位职责
2015/03/31 职场文书
圣诞晚会主持词
2015/07/01 职场文书
《百分数的认识》教学反思
2016/02/19 职场文书
python将图片转为矢量图的方法步骤
2021/03/30 Python
php7中停止php-fpm服务的方法详解
2021/05/09 PHP
Win11怎么启动任务管理器?Win11启动任务管理器的几种方法
2021/11/23 数码科技