vue keep-alive情況下切換頁面的tab並改變寬高以后發現echarts內容變成空白


vue中使用keep-alive緩存后處於disactivated狀態的echarts表格自適應功能(resize)失效的解決辦法

 

在vue開發中的時候,使用keep-alive監聽的頁面,echart或者地圖相關的模塊,測試提出了一個bug,頁面偶現空白。

經過排查發現重現步驟如下:

  離開當前頁面以后,再其他tab頁面切換了窗口大小,再回到原頁面,發現echart不顯示了,再重新改變一下窗口大小,就又恢復正常

原因:  

  之前為了實現echarts的自適應,添加了一個監聽器監測window窗口是否發生變化,如果變化了就執行echarts自帶的resize方法來改變一下echarts的大小,並且將這個方法習慣性地寫在了生成echarts的方法中。但是,我們使用了keep-alive,進行緩存后情況就有點不一樣了。我們繪制Echarts的方法是寫在mounted生命周期中的,而切換頁面並會不會進入destroyed生命周期,而是進入disactivated生命周期,這使得mounted中的函數還是激活狀態,我們之前添加的監聽器仍然在工作,但是但是但是,之前緩存頁面的dom沒了,打印出來的高度為0,我們的eventListener監聽到了別人家的dom變了,高度為0,所以圖表也為0。

  然后,當切換回已緩存的頁面時,bug就來了。echarts消失了。

  然后你再改變一下寬口大小,echarts又回來了。。。

  這是因為你之前你改變dom的時候,echarts重畫失敗了,可是你切回來的時候,dom雖然相對於你切出去之前可能變了,但是對於監聽器來說,他只關注於當前dom大小是否改變了。所以剛切回來的時候echarts的resize方法不會被激活。而只要稍稍改變一下dom的大小,就可以激活resize方法。

 

解決方案如下:

  方法一:引入element-resize-detector插件進行處理

<template>
  <div ref="chart"></div>
</template>
<script>
import elementResizeDetector from "element-resize-detector";
import * as echarts from "echarts/core";
import { LineChart, BarChart, PieChart, GraphChart, LinesChart} from "echarts/charts";
import { GridComponent, SingleAxisComponent, TooltipComponent, AxisPointerComponent, TitleComponent, LegendComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";

echarts.use([
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  SingleAxisComponent,
  GridComponent,
  AxisPointerComponent,
  BarChart,
  LineChart,
  PieChart,
  GraphChart,
  LinesChart,
  CanvasRenderer
]);

export default {
  name: "viChart",
  props: {
    options: {
      type: Object,
      required: true
    },
    isClear: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      myChart: null,
      erd: null,
    };
  },
  beforeDestroy() {
    this.dispose();
  },
  methods: {
    watchOptions() {
      this.$watch("options", this.setOption, { deep: true });
    },
    init() {
      this.myChart = echarts.init(this.$refs.chart);
      this.setOption();
    },
    resize() {
      if (!this.myChart) return;
      this.myChart.resize();
    },
    setOption() {
      if (!this.myChart || Object.keys(this.options).length === 0) return;
      if (this.isClear) {
        this.myChart.clear();
      }
      this.myChart.setOption(this.options);
      // this.resize();
    },
    dispose() {
      if (!this.myChart) return;
      this.myChart.dispose();
    }
  },
  mounted() {
    this.init();
    this.watchOptions();
    this.erd = elementResizeDetector();
    this.erd.listenTo(this.$refs.chart, this.resize);
  },
  beforeDestroy() {
    this.erd.removeListener(this.$refs.chart);
  }
};
</script>

 

  方法二:使用指令方法,監聽DOM元素寬度變化。該方法是定時循環調用時間監聽,判斷寬高變化,比較消耗資源,不建議。

<template>
  <div ref="chart" v-resize="resize"></div>
</template>
<script>
import * as echarts from "echarts/core";
import { LineChart, BarChart, PieChart, GraphChart, LinesChart} from "echarts/charts";
import { GridComponent, SingleAxisComponent, TooltipComponent, AxisPointerComponent, TitleComponent, LegendComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";

echarts.use([
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  SingleAxisComponent,
  GridComponent,
  AxisPointerComponent,
  BarChart,
  LineChart,
  PieChart,
  GraphChart,
  LinesChart,
  CanvasRenderer
]);

export default {
  name: "viChart",
  props: {
    options: {
      type: Object,
      required: true
    },
    isClear: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      myChart: null
    };
  },
  mounted() {
    this.init();
    this.watchOptions();
  },
  beforeDestroy() {
    this.unbind();
    this.dispose();
  },
  methods: {
    watchOptions() {
      this.$watch("options", this.setOption, { deep: true });
    },
    init() {
      this.myChart = echarts.init(this.$refs.chart);
      this.setOption();
    },
    unbind() {
      window.removeEventListener("resize", this.resize);
    },
    resize() {
      if (!this.myChart) return;
      this.myChart.resize();
    },
    setOption() {
      if (!this.myChart || Object.keys(this.options).length === 0) return;
      if (this.isClear) {
        this.myChart.clear();
      }
      this.myChart.setOption(this.options);
    },
    dispose() {
      if (!this.myChart) return;
      this.myChart.dispose();
    }
  },
  directives: {  // 使用局部注冊指令的方式。主要處理keep-alive情況下,切換到其他tab情況下,再切回原頁面,echart不見了
    resize: { // 指令的名稱
      bind(el, binding) { // el為綁定的元素,binding為綁定給指令的對象
        let width = ""; let height = "";
        function isReize() {
          const style = document.defaultView.getComputedStyle(el);  // 獲取對象的css樣式,返回的是一個CSS樣式對象
          if (width !== style.width || height !== style.height) {
            binding.value();  // 關鍵
          }
          width = style.width;
          height = style.height;
        }
        el.__vueSetInterval__ = setInterval(isReize, 300);
      },
      unbind(el) {
        clearInterval(el.__vueSetInterval__);
      }
    }
  },
};
</script>

  

 參考文檔:

https://blog.csdn.net/kiwon1993/article/details/102638808

https://blog.csdn.net/csl125/article/details/115075643


免責聲明!

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



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