溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

利用Vue怎么實(shí)現(xiàn)一個多頁簽組件

發(fā)布時間:2021-01-14 14:27:02 來源:億速云 閱讀:236 作者:Leah 欄目:開發(fā)技術(shù)

今天就跟大家聊聊有關(guān)利用Vue怎么實(shí)現(xiàn)一個多頁簽組件,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

<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=""
      :
     ></span>
    </div>
   </div>
  </el-scrollbar>
  <div v-show="contextMenuVisible">
   <ul
    :
    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"
      >關(guān)閉左邊</el-button
     >
    </li>
    <li>
     <el-button
      type="text"
      @click="closeOtherRight"
      :disabled="false"
      size="mini"
      >關(guān)閉右邊</el-button
     >
    </li>
    <li>
     <el-button type="text" @click="closeOther" size="mini"
      >關(guān)閉其他</el-button
     >
    </li>
   </ul>
  </div>
 </div>
</template>
<script>
export default {
 props: {
  keepAliveComponentInstance: {}, //keep-alive控件實(shí)例對象
  blankRouteName: {
   type: String,
   default: "blank",
  }, //空白路由的name值
 },
 data() {
  return {
   contextMenuVisible: false, //右鍵菜單是否顯示
   contextMenuLeft: 0, //右鍵菜單顯示位置
   contextMenuTop: 0, //右鍵菜單顯示位置
   contextMenuTargetPageRoute: null, //右鍵所指向的菜單路由
   openedPageRouters: [], //已打開的路由頁面
  };
 },
 watch: {
  //當(dāng)路由變更時,執(zhí)行打開頁面的方法
  $route: {
   handler(v) {
    this.openPage(v);
   },
   immediate: true,
  },
 },
 mounted() {
  //添加點(diǎn)擊關(guān)閉右鍵菜單
  window.addEventListener("click", this.closeContextMenu);
 },
 destroyed() {
  window.removeEventListener("click", this.closeContextMenu);
 },
 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
    );
    //判斷頁面是否支持不同參數(shù)多開頁面功能,如果不支持且已存在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);
    }
   }
  },
  //點(diǎn)擊頁面標(biāo)簽卡時
  onClick(route) {
   if (route.fullPath !== this.$route.fullPath) {
    this.$router.push(route.fullPath);
   }
  },
  //關(guān)閉頁面標(biāo)簽時
  onClose(route) {
   let index = this.openedPageRouters.indexOf(route);
   this.delPageRoute(route);
   if (route.fullPath === this.$route.fullPath) {
    //刪除頁面后,跳轉(zhuǎn)到上一頁面
    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);
    });
   }
  },
  //關(guān)閉其他頁面
  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);
   }
  },
  //根據(jù)路徑獲取索引
  getPageRouteIndex(fullPath) {
   for (let i = 0; i < this.openedPageRouters.length; i++) {
    if (this.openedPageRouters[i].fullPath === fullPath) {
     return i;
    }
   }
  },
  //關(guān)閉左邊頁面
  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--;
   }
  },
  //關(guān)閉右邊頁面
  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, 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, 0.045, 0.355, 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, 0.045, 0.355, 1);
    vertical-align: text-top;
    &:hover {
     background-color: #c0c4cc;
     color: #fff;
    }
   }
  }
 }
}
</style>

這個組件它需要兩個屬性,一個是keepAliveComponentInstance(keep-alive的控件實(shí)例對象),blankRouteName(空白路由的名稱)

為什么我需要keep-alive的控件實(shí)例對象呢,因?yàn)檫@個對象里面有兩個屬性,一個是cache,一個是keys,存儲著keep-alive的緩存的數(shù)據(jù),有了這個對象,我就能在頁簽關(guān)閉時手動刪除緩存。那這個對象怎么獲取呢,如下所示,在keep-alive所在的父頁面上的mounted事件上進(jìn)行獲取(如果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的控件實(shí)例對象
  }
 },
 data() {
  return {
   keepAliveComponentInstance: null,
  };
 }
};
</script>

而空白路由的名稱,是干什么,主要我要實(shí)現(xiàn)刷新當(dāng)前頁面的功能,我們知道vue是不允許跳轉(zhuǎn)到當(dāng)前頁面,那么我就想我先跳轉(zhuǎn)到別的頁面,再跳轉(zhuǎn)回回來的頁面,不就也實(shí)現(xiàn)刷新的效果了。(當(dāng)然我用的是relpace,所以不會產(chǎn)生歷史記錄)

注:這個空白路由并不是固定定義在根路由上,需根據(jù)多頁簽組件所在位置,假如你有一個根router-view,還有一個布局組件,這個組件里面也有一個子router-view,多頁簽組件就在這個布局組件里,那么空白路由就需定義在布局組件對應(yīng)的路由的children里面了

還有這個組件會根據(jù)路由對象的meta對象進(jìn)行不同的配置,如下所示

let router = new Router({
 routes: [
  //這個是空白頁面,重新加載當(dāng)前頁面會用到
  {
   name: "blank",
   path: "/blank",
  },
  {
   path: "/a",
   component: A,
   meta: {
    title: "A頁面", //頁面標(biāo)題
    canMultipleOpen: true //支持根據(jù)參數(shù)不同多開不同頁簽,如果你需要/a跟/a?v=123都分別打開兩個頁簽,請?jiān)O(shè)置為true,否則就只會顯示一個頁簽,后打開的會替換到前打開的頁簽
   }
  }
}

看完上述內(nèi)容,你們對利用Vue怎么實(shí)現(xiàn)一個多頁簽組件有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

vue
AI