vue使用keep-alive如何實(shí)現(xiàn)多頁(yè)簽并支持強(qiáng)制刷新
使用keep-alive實(shí)現(xiàn)多頁(yè)簽并支持強(qiáng)制刷新
需求
我司有一款使用Vue構(gòu)建的SPA 后臺(tái)管理系統(tǒng),此時(shí)客戶提出一個(gè)需求。
1:要求支持打開(kāi)多頁(yè)簽
2:多頁(yè)簽保留狀態(tài),即切換頁(yè)簽的時(shí)候不重新刷新內(nèi)容。
3:關(guān)閉某一頁(yè)簽之后,重新打開(kāi)之后,需要進(jìn)行刷新
4:在當(dāng)前頁(yè)面可以進(jìn)行強(qiáng)制刷新功能。
如圖示意(左側(cè)箭頭為多頁(yè)簽,右側(cè)為強(qiáng)制刷新按鈕,圖是網(wǎng)圖,非本公司后臺(tái)管理系統(tǒng)頁(yè)面,效果類似)
思路
1:首先編寫(xiě)Tags(多頁(yè)簽)
2:使用Keep-alive包裹 router-view。
3:利用router-view key值變化會(huì)導(dǎo)致內(nèi)容刷新來(lái)實(shí)現(xiàn)頁(yè)面強(qiáng)制刷新
已打開(kāi)菜單組件
Tags(多頁(yè)簽)左側(cè)箭頭指出功能
<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 (菜單選項(xiàng)) menulists: [ { fnHandler: 'delAll', // Binding events(綁定事件) icoName: 'fa fa-home fa-fw', // icon (icon圖標(biāo) ) btnName: '關(guān)閉所有' // The name of the menu option (菜單名稱) }, ] }, tagsList: [],//tag集合 myRouter: {}, curGo:0,// 當(dāng)前往左走的步伐,往左為正數(shù),往右為負(fù)數(shù) tagsButtonShow:false,//是不是應(yīng)該顯示左右側(cè)按鈕 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); }, //刷新當(dāng)前頁(yè) 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; }, //切換是不是顯示左右側(cè)按鈕的狀態(tài),規(guī)則:當(dāng)tagItemWapper的寬度大于overFlowWapper時(shí),顯示兩側(cè)按鈕 tagsButtonShowChange() { if(typeof this.$refs.overFlowWapper == "undefined") { this.tagsButtonShow = false; return } let owidth = this.$refs.overFlowWapper.offsetWidth; // let twidth = this.$refs.tagItemWapper.offsetWidth; 這里不能用這個(gè),這里有動(dòng)畫(huà)會(huì)延遲 let twidth = 10 + this.tagsList.length*110; if(twidth > owidth + 15) { this.tagsButtonShow = true; } else { this.tagsButtonShow = false; //測(cè)試提出:當(dāng)導(dǎo)航標(biāo)簽數(shù)量較多出現(xiàn)左右滾動(dòng)條時(shí),向右滾動(dòng)標(biāo)簽后從右邊依次關(guān)閉標(biāo)簽,當(dāng)空間足夠展示當(dāng)前打開(kāi)的所有標(biāo)簽時(shí),左側(cè)被遮擋的標(biāo)簽沒(méi)有自動(dòng)展示 this.curGo = 0; } }, goMe(item) { this.$router.push(item.path); }, delTags(index) { //工作看板不可以被關(guān)閉喲 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; }, //根據(jù)路由切換Tags標(biāo)簽 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; //是不是已經(jīng)打開(kāi)了這個(gè)標(biāo)簽 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, //詳情頁(yè) 附加名稱 queryName: (route.query.name?"-"+route.query.name : "") }) } this.changePosition(route); }, //測(cè)試提出:當(dāng)導(dǎo)航標(biāo)簽數(shù)量較多出現(xiàn)左右滾動(dòng)條時(shí),通過(guò)導(dǎo)航菜單打開(kāi)新標(biāo)簽或激活被隱藏的標(biāo)簽時(shí),沒(méi)有自動(dòng)將導(dǎo)航標(biāo)簽移動(dòng)到活動(dòng)標(biāo)簽處 changePosition(route) { let currentPosition = 1;//當(dāng)前位置,從1開(kāi)始 for(let i=0;i<this.tagsList.length;i++) { if(this.tagsList[i].name == route.name) { currentPosition = i+1; } } let currentPositionRight = 110*currentPosition-10;//當(dāng)前標(biāo)簽最右側(cè)的位置 //如果當(dāng)前標(biāo)簽最右側(cè)的位置超出了邊界,那么讓他拽出來(lái) 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() { //當(dāng)頁(yè)面分辨率發(fā)生變化時(shí),將curGo 偏移量修改為0 this.addResizeFunctions({ type: "const", name: "tags", callback: ()=>{ this.curGo = 0; this.tagsButtonShowChange(); } }); } } </script>
Home頁(yè)面
<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) {//監(jiān)聽(tīng)路由是否變化 this.showFooter = (val.name =="dashboard"?false:true); } } }, methods: { }, created(){ // 只有在標(biāo)簽頁(yè)列表里的頁(yè)面才使用keep-alive,即關(guān)閉標(biāo)簽之后就不保存到內(nèi)存中了。 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; }); // 點(diǎn)擊強(qiáng)制刷新按鈕會(huì)觸發(fā)這里。 bus.$on('keyCurName', msg => { this.bindKeys[this.$route.name] = new Date().getTime(); }); }, mounted() { }, }
使用keep-alive以后刷新部分?jǐn)?shù)據(jù)如何解決
項(xiàng)目中遇到得問(wèn)題
描述如下:
上圖頁(yè)面使用了keep-alive,所以當(dāng)從其其他頁(yè)面跳轉(zhuǎn)到這個(gè)頁(yè)面得時(shí)候不會(huì)刷新數(shù)據(jù),因此解決方式為
如下圖所示就可以
上圖中activated和created使用方法一致,在這個(gè)方法里重新獲取一下數(shù)據(jù)就好了,我們得業(yè)務(wù)場(chǎng)景是
點(diǎn)擊圖標(biāo)選取地理位置
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
簡(jiǎn)單了解vue 插值表達(dá)式Mustache
這篇文章主要介紹了vue 插值表達(dá)式Mustache的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07vue-seamless-scroll無(wú)縫滾動(dòng)組件使用方法詳解
這篇文章主要為大家詳細(xì)介紹了vue-seamless-scroll無(wú)縫滾動(dòng)組件的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04nginx+vite項(xiàng)目打包以及部署的詳細(xì)過(guò)程
我們使用nginx部署Vue項(xiàng)目,實(shí)質(zhì)上就是將Vue項(xiàng)目打包后的內(nèi)容同步到nginx指向的文件夾,下面這篇文章主要給大家介紹了關(guān)于nginx+vite項(xiàng)目打包以及部署的相關(guān)資料,需要的朋友可以參考下2023-01-01vue數(shù)據(jù)傳遞--我有特殊的實(shí)現(xiàn)技巧
這篇文章主要介紹了vue數(shù)據(jù)傳遞一些特殊梳理技巧,需要的朋友可以參考下2018-03-03淺析Proxy如何實(shí)現(xiàn)Vue響應(yīng)式
這篇文章主要是來(lái)和大家探討一下,Vue的響應(yīng)式系統(tǒng)僅僅是一個(gè)Proxy嗎,本文將圍繞此問(wèn)題探索一下Proxy是如何實(shí)現(xiàn)Vue響應(yīng)式的,感興趣的小伙伴可以了解一下2023-08-08vue 實(shí)現(xiàn)根據(jù)data中的屬性值來(lái)設(shè)置不同的樣式
這篇文章主要介紹了vue 實(shí)現(xiàn)根據(jù)data中的屬性值來(lái)設(shè)置不同的樣式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08SpringBoot+Vue前后端分離,使用SpringSecurity完美處理權(quán)限問(wèn)題的解決方法
這篇文章主要介紹了SpringBoot+Vue前后端分離,使用SpringSecurity完美處理權(quán)限問(wèn)題,需要的朋友可以參考下2018-01-01