vue中使用keep- alive緩存機制,切換標簽含有iframe標簽的界面會被重新刷新的問題處理


1、序言
最近工作中,項目上遇到一個這樣的需求,就是在打開報表的打印預覽界面時,去切換標簽,切回打印預覽界面的時候,要求界面不刷新。vue框架中,我們去處理此類問題,通常馬上就會想到去使用vue框架中自帶的keep-alive組件,所以一開始我也是去使用了keep-alive,但是發現沒有達到預期效果,后面通過研究和查閱資料發現,在vue項目中加入了含有iframe的頁面,在路由切換的過程中,使用keep-alive是達不到數據緩存的效果的。

2、使用keep-alive緩存不了iframe界面原因
(1)vue中的keep-alive
【1】原理:Vue 的緩存機制並不是直接存儲 DOM 結構,而是將 DOM 節點抽象成了一個個 VNode節點。因此,Vue 的 keep-alive 緩存也是基於 VNode節點 而不是直接存儲 DOM 節點。在需要渲染的時候從Vnode渲染到真實DOM上。
【2】參數:Keep-alive 組件提供了 include 和 exclude 兩個屬性,允許組件有條件的進行緩存。
  include: 字符串或正則表達式。只有匹配的組件會被緩存。
  exclude: 字符串或正則表達式。任何匹配的組件都不會被緩存。
【3】Keep-alive 組件提供了兩個生命鈎子函數,分別是 activated 和 deactivated 。
  activated :當頁面存在緩存的時候執行該函數。
  deactivated :在頁面結束時觸發該方法,可清除掉滾動方法等緩存。
(2)iframe中keep-alive機制失效原因:iframe頁里的內容並不屬於節點的信息,所以使用keep-alive依然會重新渲染iframe內的內容。而且iframe每一次渲染就相當於打開一個新的網頁窗口,即使把節點保存下來,在渲染時iframe頁還是刷新的。

3、vue中實現iframe內容緩存的思路
(1)保存iframe頁里的節點信息我們很難去操作,這個時候我們就該想是否能在vue的route-view節點上動些手腳。
(2)我們可以在切換不含iframe的界面時使用vue路由,在切換含iframe頁的界面時利用v-show來控制顯示隱藏,使iframe的節點不被刪除,以此來防止界面節點被重新更新,從而達到保存iframe節點數據的效果。
具體該怎么操作呢?請繼續往下看:
第一步:我們先需要修改一下路由配置,如下:

{
    path: '/printReportShow',
    component: Layout,
    redirect: '/printReportShow',
    hidden: false,
    name: 'Layout',
    children: [
      {
        path: '/printReportShow',
        iframeComponent: printReportShow,
        name: 'printReportShow',
        hidden: false,
        meta: {title: '打印預覽',}
      }
    ]
  },
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
從以上代碼,不難看出/printReportShow就是我們要跳轉的路由地址,我們可以看到printReportShow的路由配置中組件我寫的是iframeComponent而非官方教程中的component,這樣造成的后果就是在 router-view 節點中渲染時,頁面顯示的是空白頁。
第二步:修改主界面AppMain.vue(你的可能不叫這個)中的template,添加vue的內置組件component,如下:

<template>
  <section class="app-main">
    <transition name="move" mode="out-in" @enter="onEnter">
      <keep-alive :include="cachedViews">
        <router-view :key="key" />
      </keep-alive>
    </transition>
    <!--iframe頁-->
    <component
      v-for="item in hasOpenComponentsArr"
      :key="item.children[0].name"
      :is="item.children[0].name"
      v-show="$route.path === item.path"
    ></component>
  </section>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
從以上代碼我們可以看到,iframe節點數據在根節點App.vue渲染時,也隨即跟着渲染了,但是考慮到性能原因,對此我們選擇將iframe的顯示做成懶加載形式的,只有在用戶進入相應的頁面時才觸發渲染,在渲染完畢后再通過v-show去控制界面在切換時的顯示與隱藏。
第三步:AppMain.vue中具體的邏輯處理

<script>
import Vue from 'vue';
export default {
  name: 'AppMain',
  computed: {
    // 實現懶加載,只渲染已經打開過(hasOpen:true)的iframe頁
    hasOpenComponentsArr() {
      return this.componentsArr.filter(item => {
        return item.children[0].hasOpen
      });
    }
  },
  data() {
    return {
      componentsArr: []
    }
  },
  watch: {
    $route() {
      // 判斷當前路由是否iframe頁
      this.isOpenIframePage();
    }
  },
  created() {
    // 設置iframe頁的數組對象
    const componentsArr = this.getComponentsArr();
    componentsArr.forEach((item) => {
      // Vue.component(item.name, item.component);
      Vue.component(item.children[0].name, item.children[0].component);
    });
    this.componentsArr = componentsArr;
    // 判斷當前路由是否iframe頁
    this.isOpenIframePage();
  },
  methods:{
    // 根據當前路由設置hasOpen
    isOpenIframePage() {
      const target = this.componentsArr.find(item => {
        return item.path === this.$route.path
      });
      if (target && !target.children[0].hasOpen) {
        target.children[0].hasOpen = true;
      }
    },
    // 遍歷路由的所有頁面,把含有iframeComponent標識的收集起來
    getComponentsArr() {
      const router = this.$router;
      const routes = router.options.routes;
      const iframeArr = []
      const singleRoutes = routes[6];
      const printReportObj = {
        name: routes[6].children[0].name,
        path: routes[6].children[0].path,
        hasOpen: false, // 是否打開過,默認false
        component: routes[6].children[0].iframeComponent // 組件文件的引用
      }
      singleRoutes.children.splice(0,1);
      singleRoutes.children.push(printReportObj);
      iframeArr.push(singleRoutes);
      return iframeArr
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
備注:由於我們系統中就涉及到一個iframe界面,所以在處理上可能有些地方寫死了,如果時多iframe的可以自行參考着做相應的修改。

4、拓展
由於切換標簽,含iframe的component組件不會再觸發路由鈎子函數和生命周期函數,導致界面數據無法做更新操作,同時瀏覽器刷新時,界面數據會有丟失的問題,所以我們可以考慮去監聽sessionStorage的值,以此來更新數據。詳情可以看下面這篇文章:vue中如何實現對sessionStorage的監聽。

5、結語:
在這里要特別感謝jmx164491960老哥的提供的demo,感興趣的小伙伴可以點擊此處:git-vue中實現iframe頁面的數據緩存,給予了很大的幫助。
————————————————
版權聲明:本文為CSDN博主「我在小樓聽着風」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_44490109/article/details/110191582


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM