vue使用keep-alive如何实现多页签并支持强制刷新
作者:嚣张的胖头鱼
这篇文章主要介绍了vue使用keep-alive如何实现多页签并支持强制刷新,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
使用keep-alive实现多页签并支持强制刷新
需求
我司有一款使用Vue构建的SPA 后台管理系统,此时客户提出一个需求。
1:要求支持打开多页签
2:多页签保留状态,即切换页签的时候不重新刷新内容。
3:关闭某一页签之后,重新打开之后,需要进行刷新
4:在当前页面可以进行强制刷新功能。
如图示意(左侧箭头为多页签,右侧为强制刷新按钮,图是网图,非本公司后台管理系统页面,效果类似)
思路
1:首先编写Tags(多页签)
2:使用Keep-alive包裹 router-view。
3:利用router-view key值变化会导致内容刷新来实现页面强制刷新
已打开菜单组件
Tags(多页签)左侧箭头指出功能
<template> <div class="vtag" id="nTabs"> <div class="overFlowWapper" ref="overFlowWapper"> <div class="tagItemWapper" ref="tagItemWapper" v-bind:style="{ width:tagsList.length*110+'px',marginLeft:curGo+'px'}"> <div class="tagItem" v-for="(item,index) in tagsList" v-bind:class="{ active: isActive(item.path) }" @contextmenu="showMenu" @click="goMe(item)" :title="(myRouter[item.name]||item.title)+(item.queryName||'')"> <a class="leftx" v-bind:class="{canCloseNot:!item.canClose}"> {{(myRouter[item.name]||item.title)+(item.queryName||'')}} </a> <a class="rightx" v-if="item.canClose" @click.stop="delTags(index)"><span>×</span></a> <vue-context-menu :contextMenuData="contextMenuData" @delAll="delAll"> </vue-context-menu> </div> </div> </div> <div class="tagBtn tagleft" @click="tagLeft" v-if="tagsButtonShow"> <img src="static/img/svg/icon_narrow.svg"> </div> <div class="tagBtn tagRight" @click="tagRight" v-if="tagsButtonShow"> <img src="static/img/svg/icon_narrow.svg"> </div> <div class="tagBtn tagRefresh" @click="refreshCurrent"> <img src="static/img/svg/icon_refresh.svg"> </div> </div> </template>
<script> import bus from './bus'; export default { data() { return { contextMenuData: { // the contextmenu name(@1.4.1 updated) menuName: 'demo', // The coordinates of the display(菜单显示的位置) axis: { x: null, y: null }, // Menu options (菜单选项) menulists: [ { fnHandler: 'delAll', // Binding events(绑定事件) icoName: 'fa fa-home fa-fw', // icon (icon图标 ) btnName: '关闭所有' // The name of the menu option (菜单名称) }, ] }, tagsList: [],//tag集合 myRouter: {}, curGo:0,// 当前往左走的步伐,往左为正数,往右为负数 tagsButtonShow:false,//是不是应该显示左右侧按钮 defaultPage:{ path:"", name: "", canClose:false, title:"", keepAlive:true } } }, methods: { //右键菜单的使用 showMenu () { event.preventDefault(); let x = event.clientX; let y = event.clientY; // Get the current location this.contextMenuData.axis = { x, y } }, delAll() { this.tagsList = []; this.$router.push('/'); this.tagsList.push(this.defaultPage); }, //刷新当前页 refreshCurrent() { bus.$emit('keyCurName', 1); }, //按钮 tagLeft() { this.curGo = 0; }, //按钮 tagRight() { let owidth = this.$refs.overFlowWapper.offsetWidth; let twidth = this.$refs.tagItemWapper.offsetWidth; this.curGo =owidth-twidth; }, //切换是不是显示左右侧按钮的状态,规则:当tagItemWapper的宽度大于overFlowWapper时,显示两侧按钮 tagsButtonShowChange() { if(typeof this.$refs.overFlowWapper == "undefined") { this.tagsButtonShow = false; return } let owidth = this.$refs.overFlowWapper.offsetWidth; // let twidth = this.$refs.tagItemWapper.offsetWidth; 这里不能用这个,这里有动画会延迟 let twidth = 10 + this.tagsList.length*110; if(twidth > owidth + 15) { this.tagsButtonShow = true; } else { this.tagsButtonShow = false; //测试提出:当导航标签数量较多出现左右滚动条时,向右滚动标签后从右边依次关闭标签,当空间足够展示当前打开的所有标签时,左侧被遮挡的标签没有自动展示 this.curGo = 0; } }, goMe(item) { this.$router.push(item.path); }, delTags(index) { //工作看板不可以被关闭哟 if(index == 0) { return; } const delItem = this.tagsList.splice(index, 1)[0]; const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1]; if (item) { delItem.path === this.$route.fullPath && this.$router.push(item.path); }else{ this.$router.push('/'); } }, isActive(path) { return path === this.$route.fullPath; }, //根据路由切换Tags标签 setTags(route) { if(this.tagsList.length < 1) { if(this.defaultPage.name == "") { let path = this.$router.options.routes[0].redirect; let children = this.$router.options.routes[1].children; for(let i=0;i<children.length;i++) { let copy_i = children[i]; if(copy_i.path == path ) { this.defaultPage.name = copy_i.name; this.defaultPage.path = copy_i.path; this.defaultPage.title = copy_i.meta.title; this.defaultPage.keepAlive = copy_i.meta.keepAlive; } } } this.tagsList.push(this.defaultPage); } let isExist = false; //是不是已经打开了这个标签 for(let i=0;i<this.tagsList.length;i++) { if(this.tagsList[i].path === route.fullPath) { isExist = true; break; } } if(!isExist) { if(this.tagsList.length>11) { this.tagsList.splice(1,1); } this.tagsList.push({ title: (this.myRouter[route.name]||route.meta.title), path: route.fullPath, name: route.matched[1].name, keepAlive:route.meta.keepAlive, canClose:true, //详情页 附加名称 queryName: (route.query.name?"-"+route.query.name : "") }) } this.changePosition(route); }, //测试提出:当导航标签数量较多出现左右滚动条时,通过导航菜单打开新标签或激活被隐藏的标签时,没有自动将导航标签移动到活动标签处 changePosition(route) { let currentPosition = 1;//当前位置,从1开始 for(let i=0;i<this.tagsList.length;i++) { if(this.tagsList[i].name == route.name) { currentPosition = i+1; } } let currentPositionRight = 110*currentPosition-10;//当前标签最右侧的位置 //如果当前标签最右侧的位置超出了边界,那么让他拽出来 if(this.$refs.overFlowWapper) { if(currentPositionRight>this.$refs.overFlowWapper.offsetWidth) { let owidth = this.$refs.overFlowWapper.offsetWidth; let twidth = this.tagsList.length*110; this.curGo =owidth-twidth; } } }, }, computed: { }, watch:{ $route:{ immediate:true, handler:function(newValue){ this.setTags(newValue); }, deep:true }, tagsList:{ immediate:true, handler:function(newValue) { bus.$emit('keepList', this.tagsList); this.tagsButtonShowChange(); }, }, '$store.state.myRouter':{ immediate:true, handler:function(newValue){ let myRouter = {}; if(newValue != null) { for(let i=0; i<newValue.length; i++) { myRouter[newValue[i].url] = newValue[i].label; } } this.myRouter = myRouter; }, deep:true }, }, created(){ }, mounted() { //当页面分辨率发生变化时,将curGo 偏移量修改为0 this.addResizeFunctions({ type: "const", name: "tags", callback: ()=>{ this.curGo = 0; this.tagsButtonShowChange(); } }); } } </script>
Home页面
<v-tags></v-tags> <!--刚才的tags组件--> <div class="content"> <keep-alive :include="myKeepList"> <router-view :key="key"> </router-view> </keep-alive> </div>
export default { data(){ return { myKeepList: [""], bindKeys:{ addEs: "24", addWorkflow: "30", announceIssuerWatching: "45", approvalHistory: "33", approvalTask: "32", bondDetail: "50", }, } }, components:{ vTags }, computed: { key() { return this.bindKeys[this.$route.name]; }, ...mapState(['routerKey','copyRight']) }, watch:{ '$route': { immediate: true, handler: function (val) {//监听路由是否变化 this.showFooter = (val.name =="dashboard"?false:true); } } }, methods: { }, created(){ // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。 bus.$on('keepList', msg => { let arr = []; for(let i=0;i<msg.length;i++) { let copy_m = msg[i]; if(copy_m.keepAlive) { arr.push(copy_m.name); } } this.myKeepList = arr; }); // 点击强制刷新按钮会触发这里。 bus.$on('keyCurName', msg => { this.bindKeys[this.$route.name] = new Date().getTime(); }); }, mounted() { }, }
使用keep-alive以后刷新部分数据如何解决
项目中遇到得问题
描述如下:
上图页面使用了keep-alive,所以当从其其他页面跳转到这个页面得时候不会刷新数据,因此解决方式为
如下图所示就可以
上图中activated和created使用方法一致,在这个方法里重新获取一下数据就好了,我们得业务场景是
点击图标选取地理位置
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。