- 需求
在PC端開發含圖表展示的頁面時,需要考慮使用者調整瀏覽器視窗大小後圖表能夠根據視窗大小自適應變化。
- 常規做法
在含有圖表元件mounted的鉤子中增加監聽window的resize事件,具體如下程式碼所示:
<template>
<div>
<div id="chart-container"></div>
</div>
</template>
<script>
import echarts from 'echarts'
import options from "..."
export default {
data(){
return {}
},
mounted(){
let self = this
this.echartInstance = echarts.init(document.getElementById('chart-container'));
this.echartInstance.setOption(options)
window.addEventListener("resize",function(){
self.echartInstance.resize()
})
}
}
</script>
<style>
#chart-container{
width: 100%;
height: 100%;
}
</style>
複製程式碼
然而上述程式碼卻存在記憶體洩漏的風險:元件被登出時,縮放函式是匿名函式,且仍然在事件監聽列表中,因此匿名函式和匿名函式中用到的外部變數在元件登出後均不會被清理。改進方案如下:
<template>
<div>
<div id="chart-container"></div>
</div>
</template>
<script>
import echarts from 'echarts'
import options from "..."
export default {
data(){
return {}
},
mounted(){
this.echartInstance = echarts.init(document.getElementById('chart-container'));
this.echartInstance.setOption(options)
window.addEventListener("resize",this.resizeHandle)
},
methods:{
resizeHandle(){
this.echartInstance.resize()
}
},
destroyed(){
window.removeEventListener("resize",this.resizeHandle)
}
}
</script>
<style>
#chart-container{
width: 100%;
height: 100%;
}
</style>
複製程式碼
上述程式碼總算解決了記憶體洩漏的風險,但是程式碼比較冗餘。 在圖表較少時上述方案是可行的,當專案中圖表數量較多時,程式碼便會十分冗餘,如果忘了在destroyed中的清理工作還會造成記憶體洩漏的風險。
- 解決方案
圖表隨視窗大小自適應調整算是圖表元件的一個通用功能,且與具體的圖表業務無關,且其邏輯與元件的生命週期精密相關,因此想著將其封裝成一個外掛,外掛程式碼如下:
/**
* echarts 圖表自適應視窗大小變化的指令
* useage: ①main函式中引入:import '@/directive/echartResizeHelper.js'
* ②echarts容器上使用指令 <div id="chart-container" v-on-echart-resize></div>
*/
import echarts from 'echarts'
import Vue from 'vue';
export var version = '0.0.1';
var compatible = (/^2\./).test(Vue.version);
if (!compatible) {
Vue.util.warn('vue echarts resize directive ' + version + ' only supports Vue 2.x, and does not support Vue ' + Vue.version);
}
let HANDLER = "_vue_echarts_resize_handler"
function bind(el, binding){
unbind(el);
el[HANDLER]=function(){
let chart=echarts.getInstanceByDom(el);
if(!chart){
return;
}
chart.resize();
}
window.addEventListener("resize",el[HANDLER])
}
function unbind(el){
window.removeEventListener("resize",el[HANDLER]);
delete el[HANDLER];
}
var directive = {
bind: bind,
unbind: unbind
};
const onEchartResize=Vue.directive("onEchartResize",directive)
export {onEchartResize};
複製程式碼
程式碼中主要用到vue自定義指令提供的bind和unbind鉤子函式,程式碼邏輯比較簡單,這裡就不詳細解釋了。
使用上述外掛,前面的圖表元件可寫成如下方式:
<template>
<div>
<div id="chart-container" v-on-echart-resize></div>
</div>
</template>
<script>
import echarts from 'echarts'
import options from "..."
export default {
data(){
return {}
},
mounted(){
this.echartInstance = echarts.init(document.getElementById('chart-container'));
this.echartInstance.setOption(options)
}
}
</script>
<style>
#chart-container{
width: 100%;
height: 100%;
}
</style>
複製程式碼
使用該外掛的優點:
①具體業務元件中不需要處理圖表縮放功能,程式碼量較少;
②不用擔心元件銷燬時忘記清理資源
在移動端圖表專案中手機橫屏和豎屏切換時可能也存在類似需求,只需將指令中監聽的resize事件改為orientationchange事件即可複用。