基于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打造PHP的AJAX表单提交实例
Nov 03 Javascript
来自国外的14个图片放大编辑的jQuery插件整理
Oct 20 Javascript
有关JavaScript的10个怪癖和秘密分享
Aug 28 Javascript
JavaScript入门之基本函数详解
Oct 21 Javascript
JS实现动态添加DOM节点和事件的方法示例
Apr 28 Javascript
简单实现jQuery弹幕效果
May 06 jQuery
分享ES6的7个实用技巧
Jan 18 Javascript
讲解vue-router之什么是动态路由
May 28 Javascript
详解mpvue开发小程序小总结
Jul 25 Javascript
微信小程序自定义toast弹窗效果的实现代码
Nov 15 Javascript
js实现图片推拉门效果代码实例
May 18 Javascript
详解vue 组件注册
Nov 20 Vue.js
深入了解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 json_encode()函数返回json数据实例代码
2014/10/10 PHP
Laravel框架实现发送短信验证功能代码
2016/06/06 PHP
PHP中$GLOBALS['HTTP_RAW_POST_DATA']和$_POST的区别分析
2017/07/03 PHP
ASP.NET jQuery 实例2 (表单中使用回车在TextBox之间向下移动)
2012/01/13 Javascript
javascript 循环调用示例介绍
2013/11/20 Javascript
js中同步与异步处理的方法和区别总结
2013/12/25 Javascript
jquery form表单序列化为对象的示例代码
2014/03/05 Javascript
JsRender实用入门教程
2014/10/31 Javascript
JavaScript获取并更改input标签name属性的方法
2015/07/02 Javascript
Bootstrap每天必学之前端开发框架
2015/11/19 Javascript
javascript中闭包(Closure)详解
2016/01/06 Javascript
javascript的replace方法结合正则使用实例总结
2016/06/16 Javascript
Windows环境下npm install 报错: operation not permitted, rename的解决方法
2016/09/26 Javascript
使用jquery给指定的table动态添加一行、删除一行
2016/10/13 Javascript
js Dom实现换肤效果
2017/10/21 Javascript
浅谈使用mpvue开发小程序需要注意和了解的知识点
2018/05/23 Javascript
Vue动态控制input的disabled属性的方法
2018/06/26 Javascript
vue实现城市列表选择功能
2018/07/16 Javascript
如何制作一个Node命令行图像识别工具
2018/12/12 Javascript
python将MongoDB里的ObjectId转换为时间戳的方法
2015/03/13 Python
mysql 之通过配置文件链接数据库
2017/08/12 Python
浅谈python函数之作用域(python3.5)
2017/10/27 Python
tensorflow 恢复指定层与不同层指定不同学习率的方法
2018/07/26 Python
Python解决线性代数问题之矩阵的初等变换方法
2018/12/12 Python
使用npy转image图像并保存的实例
2020/07/01 Python
python Paramiko使用示例
2020/09/21 Python
Django配置跨域并开发测试接口
2020/11/04 Python
利用HTML5 Canvas制作键盘及鼠标动画的实例分享
2016/03/15 HTML / CSS
HTML5新增加标签和功能概述
2016/09/05 HTML / CSS
Origins悦木之源英国官网:雅诗兰黛集团高端植物护肤品牌
2017/11/06 全球购物
北卡罗来纳州豪华家具和家居装饰店:Carolina Rustica
2018/10/30 全球购物
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类
2012/02/06 面试题
Java基础面试题
2014/07/19 面试题
大学同学聚会邀请函
2014/01/29 职场文书
小组合作学习反思
2014/02/18 职场文书
使用ICOM IC-R9500接收机同时测评十台收音机中波接收性能
2022/05/10 无线电