Vue實現多頁籤元件
直接看效果,增加了右鍵選單,分別有重新載入、關閉左邊、關閉右邊、關閉其他功能。
也可以到我的github上看看程式碼(如果覺得這個元件有用的話,別忘了順手給個小星星)
程式碼:https://github.com/Caijt/VuePageTab
演示:https://caijt.github.io/VuePageTab/
我這個多頁籤元件裡面的刪除快取的方法不是使用keep-alive元件自帶的include、exculde結合的效果,而是使用暴力刪除快取的方法,這個在上個部落格中也有提到,用這種方法的話,可以實現更完整的多頁籤功能,例如同個路由可以根據引數的不同同時開啟不同的頁籤,也能不用去寫那些路由的name值。
先直接看元件程式碼(裡面用了一些element-ui的元件,如果你們不用element-ui的話。可以去掉,自己實現)
<template> <div class="__common-layout-pageTabs"> <el-scrollbar> <div class="__tabs"> <div class="__tab-item" v-for="item in openedPageRouters" :class="{ '__is-active': item.fullPath == $route.fullPath,}" :key="item.fullPath" @click="onClick(item)" @contextmenu.prevent="showContextMenu($event,item)" > {{ item.meta.title }} <span class="el-icon-close" @click.stop="onClose(item)" @contextmenu.prevent.stop="" :style="openedPageRouters.length <= 1 ? 'width:0;' : ''" ></span> </div> </div> </el-scrollbar> <div v-show="contextMenuVisible"> <ul :style="{ left: contextMenuLeft + 'px',top: contextMenuTop + 'px' }" class="__contextmenu" > <li> <el-button type="text" @click="reload()" size="mini"> 重新載入 </el-button> </li> <li> <el-button type="text" @click="closeOtherLeft" :disabled="false" size="mini" >關閉左邊</el-button > </li> <li> <el-button type="text" @click="closeOtherRight" :disabled="false" size="mini" >關閉右邊</el-button > </li> <li> <el-button type="text" @click="closeOther" size="mini" >關閉其他</el-button > </li> </ul> </div> </div> </template> <script> export default { props: { keepAliveComponentInstance: {},//keep-alive控制元件例項物件 blankRouteName: { type: String,default: "blank",},//空白路由的name值 },data() { return { contextMenuVisible: false,//右鍵選單是否顯示 contextMenuLeft: 0,//右鍵選單顯示位置 contextMenuTop: 0,//右鍵選單顯示位置 contextMenuTargetPageRoute: null,//右鍵所指向的選單路由 openedPageRouters: [],//已開啟的路由頁面 }; },watch: { //當路由變更時,執行開啟頁面的方法 $route: { handler(v) { this.openPage(v); },immediate: true,mounted() { //新增點選關閉右鍵選單 window.addEventListener("click",this.closeContextMenu); },destroyed() { window.removeEventListener("click",methods: { //開啟頁面 openPage(route) { if (route.name == this.blankRouteName) { return; } let isExist = this.openedPageRouters.some( (item) => item.fullPath == route.fullPath ); if (!isExist) { let openedPageRoute = this.openedPageRouters.find( (item) => item.path == route.path ); //判斷頁面是否支援不同引數多開頁面功能,如果不支援且已存在path值一樣的頁面路由,那就替換它 if (!route.meta.canMultipleOpen && openedPageRoute != null) { this.delRouteCache(openedPageRoute.fullPath); this.openedPageRouters.splice( this.openedPageRouters.indexOf(openedPageRoute),1,route ); } else { this.openedPageRouters.push(route); } } },//點選頁面標籤卡時 onClick(route) { if (route.fullPath !== this.$route.fullPath) { this.$router.push(route.fullPath); } },//關閉頁面標籤時 onClose(route) { let index = this.openedPageRouters.indexOf(route); this.delPageRoute(route); if (route.fullPath === this.$route.fullPath) { //刪除頁面後,跳轉到上一頁面 this.$router.replace( this.openedPageRouters[index == 0 ? 0 : index - 1] ); } },//右鍵顯示選單 showContextMenu(e,route) { this.contextMenuTargetPageRoute = route; this.contextMenuLeft = e.layerX; this.contextMenuTop = e.layerY; this.contextMenuVisible = true; },//隱藏右鍵選單 closeContextMenu() { this.contextMenuVisible = false; this.contextMenuTargetPageRoute = null; },//過載頁面 reload() { this.delRouteCache(this.contextMenuTargetPageRoute.fullPath); if (this.contextMenuTargetPageRoute.fullPath === this.$route.fullPath) { this.$router.replace({ name: this.blankRouteName }).then(() => { this.$router.replace(this.contextMenuTargetPageRoute); }); } },//關閉其他頁面 closeOther() { for (let i = 0; i < this.openedPageRouters.length; i++) { let r = this.openedPageRouters[i]; if (r !== this.contextMenuTargetPageRoute) { this.delPageRoute(r); i--; } } if (this.contextMenuTargetPageRoute.fullPath != this.$route.fullPath) { this.$router.replace(this.contextMenuTargetPageRoute); } },//根據路徑獲取索引 getPageRouteIndex(fullPath) { for (let i = 0; i < this.openedPageRouters.length; i++) { if (this.openedPageRouters[i].fullPath === fullPath) { return i; } } },//關閉左邊頁面 closeOtherLeft() { let index = this.openedPageRouters.indexOf( this.contextMenuTargetPageRoute ); let currentIndex = this.getPageRouteIndex(this.$route.fullPath); if (index > currentIndex) { this.$router.replace(this.contextMenuTargetPageRoute); } for (let i = 0; i < index; i++) { let r = this.openedPageRouters[i]; this.delPageRoute(r); i--; index--; } },//關閉右邊頁面 closeOtherRight() { let index = this.openedPageRouters.indexOf( this.contextMenuTargetPageRoute ); let currentIndex = this.getPageRouteIndex(this.$route.fullPath); for (let i = index + 1; i < this.openedPageRouters.length; i++) { let r = this.openedPageRouters[i]; this.delPageRoute(r); i--; } if (index < currentIndex) { this.$router.replace(this.contextMenuTargetPageRoute); } },//刪除頁面 delPageRoute(route) { let routeIndex = this.openedPageRouters.indexOf(route); if (routeIndex >= 0) { this.openedPageRouters.splice(routeIndex,1); } this.delRouteCache(route.fullPath); },//刪除頁面快取 delRouteCache(key) { let cache = this.keepAliveComponentInstance.cache; let keys = this.keepAliveComponentInstance.keys; for (let i = 0; i < keys.length; i++) { if (keys[i] == key) { keys.splice(i,1); if (cache[key] != null) { delete cache[key]; } break; } } },}; </script> <style lang="scss"> .__common-layout-pageTabs { .__contextmenu { // width: 100px; margin: 0; border: 1px solid #e4e7ed; background: #fff; z-index: 3000; position: absolute; list-style-type: none; padding: 5px 0; border-radius: 4px; font-size: 14px; color: #333; box-shadow: 1px 1px 3px 0 rgba(0,0.1); li { margin: 0; padding: 0px 15px; &:hover { background: #f2f2f2; cursor: pointer; } button { color: #2c3e50; } } } $c-tab-border-color: #dcdfe6; position: relative; &::before { content: ""; border-bottom: 1px solid $c-tab-border-color; position: absolute; left: 0; right: 0; bottom: 0; height: 100%; } .__tabs { display: flex; .__tab-item { white-space: nowrap; padding: 8px 6px 8px 18px; font-size: 12px; border: 1px solid $c-tab-border-color; border-left: none; border-bottom: 0px; line-height: 14px; cursor: pointer; transition: color 0.3s cubic-bezier(0.645,0.045,0.355,1),padding 0.3s cubic-bezier(0.645,1); &:first-child { border-left: 1px solid $c-tab-border-color; border-top-left-radius: 2px; margin-left: 10px; } &:last-child { border-top-right-radius: 2px; margin-right: 10px; } &:not(.__is-active):hover { color: #409eff; .el-icon-close { width: 12px; margin-right: 0px; } } &.__is-active { padding-right: 12px; border-bottom: 1px solid #fff; color: #409eff; .el-icon-close { width: 12px; margin-right: 0px; margin-left: 2px; } } .el-icon-close { width: 0px; height: 12px; overflow: hidden; border-radius: 50%; font-size: 12px; margin-right: 12px; transform-origin: 100% 50%; transition: all 0.3s cubic-bezier(0.645,1); vertical-align: text-top; &:hover { background-color: #c0c4cc; color: #fff; } } } } } </style>
這個元件它需要兩個屬性,一個是keepAliveComponentInstance(keep-alive的控制元件例項物件),blankRouteName(空白路由的名稱)
為什麼我需要keep-alive的控制元件例項物件呢,因為這個物件裡面有兩個屬性,一個是cache,一個是keys,儲存著keep-alive的快取的資料,有了這個物件,我就能在頁籤關閉時手動刪除快取。那這個物件怎麼獲取呢,如下所示,在keep-alive所在的父頁面上的mounted事件上進行獲取(如果keep-alive跟多頁籤元件不在同一個父頁面,那可能就得借用vuex來傳值了)
<template> <div id="app"> <page-tabs :keep-alive-component-instance="keepAliveComponentInstance" /> <div ref="keepAliveContainer"> <keep-alive> <router-view :key="$route.fullPath" /> </keep-alive> </div> </div> </template> <script> import pageTabs from "./components/pageTabs.vue"; export default { name: "App",components: { pageTabs,mounted() { if (this.$refs.keepAliveContainer) { this.keepAliveComponentInstance = this.$refs.keepAliveContainer.childNodes[0].__vue__;//獲取keep-alive的控制元件例項物件 } },data() { return { keepAliveComponentInstance: null,}; } }; </script>
而空白路由的名稱,是幹什麼,主要我要實現重新整理當前頁面的功能,我們知道vue是不允許跳轉到當前頁面,那麼我就想我先跳轉到別的頁面,再跳轉回回來的頁面,不就也實現重新整理的效果了。(當然我用的是relpace,所以不會產生歷史記錄)
注:這個空白路由並不是固定定義在根路由上,需根據多頁籤元件所在位置,假如你有一個根router-view,還有一個佈局元件,這個元件裡面也有一個子router-view,多頁籤元件就在這個佈局元件裡,那麼空白路由就需定義在佈局元件對應的路由的children裡面了
還有這個元件會根據路由物件的meta物件進行不同的配置,如下所示
let router = new Router({ routes: [ //這個是空白頁面,重新載入當前頁面會用到 { name: "blank",path: "/blank",{ path: "/a",component: A,meta: { title: "A頁面",//頁面標題 canMultipleOpen: true //支援根據引數不同多開不同頁籤,如果你需要/a跟/a?v=123都分別開啟兩個頁籤,請設定為true,否則就只會顯示一個頁籤,後開啟的會替換到前開啟的頁籤 } } }
以上就是Vue實現多頁籤元件的詳細內容,更多關於Vue實現多頁籤元件的資料請關注我們其它相關文章!