在 vue 項目使用 echarts 的場景中,以下三點不容忽視:1. 可視化的數(shù)據(jù)往往是異步加載的;2. 若一個頁面存在大量的圖表( 尤其當存在關(guān)系圖和地圖時 ),往往會導(dǎo)致該頁面的渲染速度很慢并可能在幾秒內(nèi)卡死,產(chǎn)生極差的用戶體驗。3. 引入 echarts 組件導(dǎo)致編譯后的文件過大從而使得首次訪問的加載極慢。關(guān)于第三點,大家可以參考之前的撰文 優(yōu)化 Vue 項目編譯文件大小。以下針對上述前兩點,給出數(shù)據(jù)異步、延遲渲染的 echarts vue 組件的設(shè)計和實現(xiàn)方式,并對實現(xiàn)之中可能存在的問題進行介紹。
1. 抽離 echarts 公共部分形成基礎(chǔ)組件
1.1 調(diào)研公共部分
首先,我們需要把 echarts 使用中公共的部分抽離出來,形成基礎(chǔ)組件。
讓我們在 官網(wǎng) - 5 分鐘上手 ECharts 教程中找到使用 echarts 的步驟:
# 1. 獲取一個用于掛在 echarts 的 DOM 元素
let $echartsDOM = document.getElementById('echarts-dom')
# 2. 初始化
let myEcharts = echarts.init($echartsDOM)
# 3. 設(shè)置配置項
let option = {...}
# 4. 為 echarts 指定配置
myEcharts.setOption(option)
注:在 Vue 中,首先我們需要使用 import echarts from 'echarts' 以引入 echarts。
由上可知,在 echarts 使用中,除第三步設(shè)置配置項以外,其他的步驟都是重復(fù)的,即可以抽離出來放入組件中統(tǒng)一實現(xiàn)。
注:其實 option 配置中也存在可以抽離的部分,比如我們可以將 echarts 的顏色、散點大小、折線粗細等提取出來統(tǒng)一賦值,以保證 echarts 風格的統(tǒng)一。但由于不同類型的 ehcarts 圖的顏色配置方式不同,因而實現(xiàn)起來相對繁瑣,這里不進行說明,有興趣的同學(xué)可以自行嘗試。
1.2 實現(xiàn) echarts 功能
首先我們書寫一個簡單 ehcart.vue,其中,配置項直接復(fù)制于官網(wǎng)的教程示例。
<style scoped>
.echarts {
width: 100%;
height: 100%;
}
</style>
<template>
<div>
<div class="echarts" id="echarts-dom"></div>
</div>
</template>
<script>
import echarts from 'echarts'
export default {
name: 'echarts',
data() {
return {}
},
mounted() {
let $echartsDOM = document.getElementById('echarts-dom')
let myEcharts = echarts.init($echartsDOM)
let option = {
title: {
text: 'ECharts 入門示例'
},
tooltip: {},
legend: {
data: ['銷量']
},
xAxis: {
data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
},
yAxis: {},
series: [{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
}
myEcharts.setOption(option)
}
}
</script>
然后在App.vue中引入這一組件,并設(shè)置 echarts 的寬高:
<style>
.echarts-container{
width: 100%;
height: 20rem;
}
</style>
<template>
<div id="app">
<i-echart class="echarts-container"></i-echart>
</div>
</template>
<script>
import iEchart from './components/echart'
export default {
name: 'app',
components: {
iEchart
}
}
</script>
刷新頁面后,即可看到柱狀圖。
1.3 組件化
由于我們需要抽離 option 部分,最好的方式是將其作為組件的屬性,即 props 交由調(diào)用方配置:
# echart.vue
import echarts from 'echarts'
export default {
name: 'echarts',
props: {
option: {
type: Object,
default(){
return {}
}
}
},
data() {
return {}
},
mounted() {
let $echartsDOM = document.getElementById('echarts-dom')
let myEcharts = echarts.init($echartsDOM)
let option = this.option
myEcharts.setOption(option)
}
}
1.4 調(diào)用組件
然后我們可以將 option 配置抽離到組件調(diào)用方,并通過「傳參」的方式進行調(diào)用:
<i-echart :option="option" class="echarts-container"></i-echart>
1.5 提高組件強壯型
之前我們注意到,在 option 參數(shù)中,我們給出了默認值 {},即空對象。這樣做其實是有問題的,即在 echarts 中,如果傳入的 option 配置對象不含有 series 鍵,就會拋出錯誤:
Error: Option should contains series.
默認值處理是需要存在的,即當調(diào)用方傳入的對象為空或不存在 series 配置時,應(yīng)在頁面上顯示一些提示( 對用戶友好的提示,而不是對編程人員 ),即避免因報錯而造成空白的情況。
此外,當我們像之前那樣給 option 這一參數(shù)進行類型限制后,倘若調(diào)用方傳入非對象類型,Vue 會直接拋出錯誤——這一結(jié)果也不是我們想要的。我們應(yīng)該取消類型限制,并在 option 發(fā)生變化時進行依次以下判斷:
1. 是否為對象;
2. 是否為空對象;
3. 是否包含 series 鍵;
4. series 是否為數(shù)組;
5. series 數(shù)組是否為空。
代碼實現(xiàn)如下:
function isValidOption(option){
return isObject(option) && !isEmptyObject(option)
&& hasSeriesKey(option)
&& isSeriesArray(option) && !isSeriesEmpty(option)
}
function isObject(option) {
return Object.prototype.isPrototypeOf(option)
}
function isEmptyObject(option){
return Object.keys(option).length === 0
}
function hasSeriesKey(option){
return !!option['series']
}
function isSeriesArray(option) {
return Array.isArray(option['series'])
}
function isSeriesEmpty(option){
return option['series'].length === 0
}
注:實際上,當判斷出 option 為對象后,可以直接進行第三步的判斷。
然后,當判斷 option 符合上述三種情況時,在頁面上顯示如「數(shù)據(jù)為空」之類的提示:
import echarts from 'echarts'
export default {
name: 'echarts',
props: {
option: {
default(){
return {}
}
}
},
data() {
return { }
},
mounted() {
//# 1. 獲取一個用于掛在 echarts 的 DOM 元素
let $echartsDOM = document.getElementById('echarts-dom')
//# 2. 初始化
let myEcharts = echarts.init($echartsDOM)
//# 3. 設(shè)置配置項 let option = {...}
//# 4. 為 echarts 指定配置 myEcharts.setOption(option)
this.myEcharts = myEcharts
this.checkAndSetOption()
},
watch: {
option(option){
this.checkAndSetOption()
}
},
methods: {
checkAndSetOption(){
let option = this.option //配置等于父組件傳過來的數(shù)據(jù)
if(isValidOption(option)){
this.myEcharts.setOption(option); //渲染出來
this.myEcharts.hideLoading(); //隱藏加載動畫
}else{
this.myEcharts.showLoading(); //加載動畫
}
}
}
}
這里在書寫代碼時,有以下幾點需要注意:
-
1、我們對 DOM 元素獲取結(jié)果做了校驗,即當 option 不符合要求時,ID 為 echarts-dom 的 DOM 元素是不存在的,此時
document.getElementById()的返回結(jié)果為空,不能直接使用echarts.init(),否則會拋出錯誤:Error: Initialize failed: invalid dom; -
2、在 Vue 中,初始化的值不會被 watch 鉤子捕捉,從而導(dǎo)致組件被調(diào)用方調(diào)用并賦予 option 參數(shù)時不會進入校驗。雖然可以使用
immediate: true使得watch鉤子能夠在屬性初始化賦值時被觸發(fā),但這樣做是不合適的。因為這樣設(shè)置之后,在 option 初始化從而觸發(fā) watch 時,用于掛載 echarts 的 DOM 元素還未存在于頁面中,從而導(dǎo)致出現(xiàn)TypeError: Cannot read property 'setOption' of null的錯誤。我們要重點注意 echarts 作用的生命周期,這一點后續(xù)還會涉及。
1.6 增強組件功能 - 數(shù)據(jù)加載提示
在實際場景中,用于渲染的數(shù)據(jù)常常是異步獲取的,在異步加載數(shù)據(jù)之中,我們可能需要在頁面中顯示如「正在加載...」的字樣來表示加載過程正在進行以提高用戶體驗。而加載過程就組件而言是無法直接獲取的,所以,我們需要使用某一參數(shù)用于進行加載信息的顯示
ECharts 默認有提供了一個簡單的加載動畫。只需要調(diào)用 showLoading 方法顯示。數(shù)據(jù)加載完成后再調(diào)用 hideLoading 方法隱藏加載動畫。
//在App.vue中模擬3秒后獲取數(shù)據(jù)
data() {
return {
option: {}
}
},
created(){
setTimeout(()=>{
this.option={
title: {
text: 'ECharts 入門示例'
},
tooltip: {},
legend: {
data: ['銷量']
},
xAxis: {
data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
},
yAxis: {},
series: [{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
}
console.log(this.option);
},3000)
}
然后就可以在echarts組件里調(diào)用了
methods: {
checkAndSetOption(){
let option = this.option //配置等于父組件傳過來的數(shù)據(jù)
if(isValidOption(option)){
this.myEcharts.setOption(option); //渲染出來
this.myEcharts.hideLoading(); //隱藏加載動畫
}else{
this.myEcharts.showLoading(); //加載動畫
}
}
}
1.7 增強組件功能 - 數(shù)據(jù)不合法提示
當傳入的 option 值不符合規(guī)定時?;谶@一標識,我們可以對 echarts 組件進行優(yōu)化,當 option 不合法或數(shù)據(jù)為空時給出提示信息而不是顯示空白甚至報錯。