基于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 相关文章推荐
使用AngularJS 应用访问 Android 手机的图片库
Mar 24 Javascript
在JavaScript中处理字符串之fontcolor()方法的使用
Jun 08 Javascript
JavaScript和JQuery的鼠标mouse事件冒泡处理
Jun 19 Javascript
全面解析Bootstrap中form、navbar的使用方法
May 30 Javascript
Vue.js每天必学之数据双向绑定
Sep 05 Javascript
详解微信小程序开发之——wx.showToast(OBJECT)的使用
Jan 18 Javascript
javascript作用域链与执行环境详解
Mar 25 Javascript
Vue中使用vux配置代码详解
Sep 16 Javascript
基于Vue实现平滑过渡的拖拽排序功能
Jun 12 Javascript
Vue 页面权限控制和登陆验证功能的实例代码
Jun 20 Javascript
layui字体图标 loading图标静止不旋转的解决方法
Sep 23 Javascript
jQuery实现弹出层效果
Dec 10 jQuery
深入了解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写入、删除与复制文件的方法
2015/06/20 PHP
jQuery 打造动态下滑菜单实现说明
2010/04/15 Javascript
node.js中的events.emitter.once方法使用说明
2014/12/10 Javascript
javascript 获取浏览器版本
2015/01/21 Javascript
JQuery中绑定事件(bind())和移除事件(unbind())
2015/02/27 Javascript
原生JS实现旋转木马式图片轮播插件
2016/04/25 Javascript
一次$.getJSON不执行的简单记录
2016/07/19 Javascript
jQuery点击导航栏选中更换样式的实现代码
2017/01/23 Javascript
js字符限制(字符截取) 一个中文汉字算两个字符
2017/09/12 Javascript
vue.js或js实现中文A-Z排序的方法
2018/03/08 Javascript
vue组件实现可搜索下拉框扩展
2020/10/23 Javascript
JS散列表碰撞处理、开链法、HashTable散列示例
2019/02/08 Javascript
layer.js之回调销毁对话框的例子
2019/09/11 Javascript
Vue插槽_特殊特性slot,slot-scope与指令v-slot说明
2020/09/04 Javascript
微信小程序实现点击页面出现文字
2020/09/21 Javascript
Python获取昨天、今天、明天开始、结束时间戳的方法
2018/06/01 Python
Python做智能家居温湿度报警系统
2018/09/25 Python
利用python修改json文件的value方法
2018/12/31 Python
对Python+opencv将图片生成视频的实例详解
2019/01/08 Python
使用Python操作FTP实现上传和下载的方法
2019/04/01 Python
Python识别快递条形码及Tesseract-OCR使用详解
2019/07/15 Python
Python 图像对比度增强的几种方法(小结)
2019/09/25 Python
pandas 对group进行聚合的例子
2019/12/27 Python
基于python调用jenkins-cli实现快速发布
2020/08/14 Python
如何让PyQt5中QWebEngineView与JavaScript交互
2020/10/21 Python
新加坡领先的时尚生活方式零售品牌:CHARLES & KEITH
2018/01/16 全球购物
加拿大时尚潮流大码女装购物网站:Addition Elle
2018/04/02 全球购物
介绍一下Java中标识符的命名规则
2014/02/03 面试题
自考自我鉴定范文
2013/10/30 职场文书
学生安全教育材料
2014/02/14 职场文书
大学同学十年聚会感言
2014/02/21 职场文书
风险评估实施方案
2014/03/09 职场文书
超市促销活动总结
2014/07/01 职场文书
唐山大地震的观后感
2015/06/05 职场文书
Mysql systemctl start mysqld报错的问题解决
2021/06/03 MySQL
java中用float时,数字后面加f,这样是为什么你知道吗
2021/09/04 Java/Android