在vue下封裝echarts公共組件的總結(jié)

這幾天公司里邊有一個(gè)項(xiàng)目曲掰,叫做日控制臺(tái)蛔琅,該項(xiàng)目是在webview下的一個(gè)webapp胎许,使用vue構(gòu)建,項(xiàng)目中要求使用許多自定義的圖表罗售」家ぃ考察了許多圖表組件之后,發(fā)現(xiàn)echarts是所有表庫(kù)中寨躁,最靈活穆碎,特效最好看的一種。

一职恳、構(gòu)建基礎(chǔ)公共組件

1. 實(shí)現(xiàn)基礎(chǔ)功能

在echart官網(wǎng)上搜索到所禀,如何使用

# 1. 獲取一個(gè)用于掛在 echarts 的 DOM 元素
let $echartsDOM = document.getElementById('echarts-dom')

# 2. 初始化
let myEcharts = echarts.init($echartsDOM)

# 3. 設(shè)置配置項(xiàng)
let option = {...}

# 4. 為 echarts 指定配置
myEcharts.setOption(option)

使用echart的步驟也就這幾部,就是先獲取到承載echart實(shí)例的dom實(shí)例话肖,然后調(diào)用init()方法初始化圖標(biāo)實(shí)例北秽,最后調(diào)用setOption()方法傳入配置項(xiàng)
這幾步都要在vue的mounted方法下實(shí)現(xiàn).

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)
  }
}

注:在 Vue 中,首先我們需要使用 import echarts from 'echarts' 以引入 echarts最筒。

二、組件化

思路很簡(jiǎn)單蔚叨,就是將業(yè)務(wù)上用到的圖表床蜘,比如柱狀圖、折線圖蔑水,通通封裝成組件邢锯,然后再main app文件中調(diào)用,通過(guò)分析搀别,可以通過(guò)傳props丹擎,來(lái)改變setOption()方法中的對(duì)象,達(dá)到封裝不同圖表組件的目的歇父。

 <ocEcharts class="echarts-container" :options="ocoptions" />

將之前的options轉(zhuǎn)移到main app中的data對(duì)象之中蒂培。
注:echarts圖表要求承載的容器具有固定的寬高才能正常顯示

.echarts-container{
  width: 100%;
  height: 20rem;
}

1. 組件優(yōu)化-props的series校驗(yàn)

如果在傳入組件的props中傳入了空對(duì)象,就會(huì)發(fā)現(xiàn)榜苫,圖表會(huì)拋出一個(gè)錯(cuò)誤护戳,即

Error: Option should contains series.

原因就是傳入的 option 配置對(duì)象不含有 series 鍵,所以垂睬,默認(rèn)值處理是需要存在的媳荒,即當(dāng)調(diào)用方傳入的對(duì)象為空或不存在 series 配置時(shí)抗悍,應(yīng)在頁(yè)面上顯示一些提示( 對(duì)用戶友好的提示,而不是對(duì)編程人員 )钳枕,即避免因報(bào)錯(cuò)而造成空白的情況缴渊。
此外,當(dāng)我們像之前那樣給 option 這一參數(shù)進(jìn)行類型限制后鱼炒,倘若調(diào)用方傳入非對(duì)象類型衔沼,Vue 會(huì)直接拋出錯(cuò)誤——這一結(jié)果也不是我們想要的。我們應(yīng)該取消類型限制田柔,并在 option 發(fā)生變化時(shí)進(jìn)行依次以下判斷:

// 1. 是否為對(duì)象俐巴;
export function isObject(option) {
  return Object.prototype.isPrototypeOf(option)
}

// 2. 是否為空對(duì)象;
export function isEmptyObject(option) {
  return Object.keys(option).length === 0
}

// 3. 是否包含 series 鍵硬爆;
export function hasSeriesKey(option) {
  return !!option['series']
}

// 4. series 是否為數(shù)組欣舵;
export function isSeriesArray(option) {
  return Array.isArray(option['series'])
}

// 5. series 數(shù)組是否為空。
export function isSeriesEmpty(option) {
  return option['series'].length === 0
}

export function isValidOption(option) {
  return isObject(option) && !isEmptyObject(option)
    && hasSeriesKey(option)
    && isSeriesArray(option) && !isSeriesEmpty(option)
}

然后在組件中引入最后的isValidOption方法作為判斷缀磕,我們先使用一個(gè)watch監(jiān)聽options的變化

 watch: {
    options(options){
      this.checkAndSetOption()
    }
  },
  methods: {
    checkAndSetOption(){
      let options = this.options
      if(isValidOption(options)){
        this.myEcharts.setOption(options)
        this.isOptionAbnormal = false
      }else{
        this.isOptionAbnormal = true
      }
    }
  }

這里的checkAndSetOption方法是判斷傳入的option是否合法缘圈,如果合法,就執(zhí)行setOption袜蚕,isOptionAbnormal變量是監(jiān)控頁(yè)面是否顯示options非法的flag糟把。
同樣的dom樣式上的改變,也要使用v-show來(lái)判斷isOptionAbnormal是否要渲染圖表和渲染錯(cuò)誤信息牲剃。

<div>
    <div class="shadow" v-show="isOptionAbnormal">數(shù)據(jù)為空</div>
    <div class="oc_echarts_container">
      <div class="echarts" id="echarts-dom" v-show="!isOptionAbnormal" />
    </div>
</div>

.oc_echarts_container, .echarts {
  width: 100%;
  height: 100%;
}

.shadow {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1rem;
  color: #8590a6;
}

2. 增強(qiáng)組件功能 - 數(shù)據(jù)加載提示

<div class="loading" v-show="isLoading">
        數(shù)據(jù)加載中...
 </div>

在組件內(nèi)部需要一個(gè)外部的props遣疯,isLoading,但是這里需要注意凿傅,在 Vue 中缠犀,v-show 使用 display 控制組件的顯隱。而當(dāng) echart init 的時(shí)候聪舒,如果其掛載 DOM 的 v-show 處于 false 狀態(tài)辨液,則其 init 的對(duì)象寬高都是 0。即使之后 v-show 狀態(tài)改變箱残,由于 mounted 生命周期不會(huì)再次觸發(fā)滔迈,從而使得 echarts 顯示不正常。所以需要使用css的visibility來(lái)控制顯隱被辑。

computed: {
    isChartVisible() {
       return !this.isLoading && !this.isOptionAbnormal
    }
  },
 <div class="oc_echarts_container">
      <div class="echarts" id="echarts-dom" :style="{visibility: isChartVisible ? 'visible' : 'hidden'}" />
    </div>

3. 組件復(fù)用-隨機(jī)ID

echarts 進(jìn)行 init 掛載時(shí)使用的是 DOM 元素的 ID燎悍。而在組件中,我們?cè)O(shè)置的 ID 是固定的( 注意與 scoped css 進(jìn)行區(qū)分 )敷待。如果多個(gè)組件的 ID 是相同的间涵,只有一個(gè)組件會(huì)被 echarts 掛載。
所以我們要設(shè)定一個(gè)隨機(jī)的randomId,賦值到承載echarts圖表的dom元素的id中

<div class="oc_echarts_container">
      <div class="echarts" :id="randomId" :style="{visibility: isChartVisible ? 'visible' : 'hidden'}" />
    </div>

三榜揖、延遲加載

延遲加載是組件的一個(gè)優(yōu)化勾哩,在業(yè)務(wù)的開發(fā)中可以看到抗蠢,一個(gè)頁(yè)面往往有著許多的圖表,圖標(biāo)伴隨著許多異步請(qǐng)求和canvas渲染思劳,如果一次性渲染所有的圖表會(huì)導(dǎo)致許多的性能問(wèn)題迅矛。這里想到的一個(gè)解決方案就是延遲加載。
用通俗的話講就是潜叛,頁(yè)面滾動(dòng)到哪張圖表就去渲染哪一張圖表秽褒。
完成這一功能需要以下步驟:

  1. 監(jiān)聽頁(yè)面滾動(dòng)事件;
  2. 滾動(dòng)事件中獲取 echarts 的位置威兜;
  3. 在頁(yè)面當(dāng)前位置達(dá)到 echarts 位置的時(shí)候進(jìn)行 echarts 的初始化销斟。

1. 監(jiān)聽頁(yè)面滾動(dòng)

如果要監(jiān)聽頁(yè)面滾動(dòng),需要用到dom的監(jiān)聽器椒舵,addEventListener('scroll', [funciton])蚂踊。這樣就能為每一個(gè)組件附上監(jiān)聽事件。

2. 獲取當(dāng)前滾動(dòng)下邊界和組件的上邊界

這一個(gè)步驟需要封裝成一個(gè)函數(shù)笔宿,checkPosition()

checkPosition() {
      let windowHeight = document.documentElement.clientHeight||window.innerHeight
      let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
      let windowBottom = scrollTop + windowHeight
      const selfTop = _.get(this.$refs, 'selfEcharts.offsetTop', 0);
      if(windowBottom >= selfTop) {
        this.isPositionReady = true
        this.checkAndSetOption()
        window.removeEventListener('scroll',  this.scrollEvent)
      }
    },
  • windowBottom(滑動(dòng)的下邊界) = scrollTop(屏幕當(dāng)前滑動(dòng)的距離) + windowHeight(窗口的高度)
  • selfTop(當(dāng)前組件的頂部位置)
    當(dāng)?shù)谝粋€(gè)變量大于第二個(gè)變量時(shí)犁钟,就認(rèn)為滑動(dòng)到了該圖表組件,就開開啟加載

3. 初始化

data() {
    return {
      myEcharts: null,
      isOptionAbnormal: false,
      randomId: 'echarts-dom' + Date.now() + Math.random(),
      scrollEvent:  _.throttle(this.checkPosition, 500), // 滑動(dòng)事件
      isPositionReady: false, //控制數(shù)據(jù)異步與頁(yè)面滾動(dòng)先后順序的flag
    }
  },
mounted() {
    let $echartsDOM = document.getElementById(this.randomId)
    if(!$echartsDOM) return
    this.myEcharts = echarts.init($echartsDOM)
        // 第一次未滑動(dòng)的時(shí)候
    this.checkPosition()
        //滑動(dòng)之后的監(jiān)聽
    window.addEventListener('scroll', this.scrollEvent)
  },

checkPosition方法不僅要在scroll監(jiān)聽事件中調(diào)用泼橘,在組件第一次渲染的時(shí)候也要調(diào)用一次進(jìn)行初始化涝动,不然,組件無(wú)法正常渲染圖表炬灭。

4.節(jié)流

組件代碼經(jīng)過(guò)測(cè)試之后發(fā)現(xiàn)醋粟,如果滾動(dòng)過(guò)于快速,會(huì)不停的調(diào)用checkPosition(),關(guān)鍵是這個(gè)方法會(huì)不停的去setOption()重归,所以以上代碼均采用了lodash的throttle節(jié)流方法昔穴,在500毫秒內(nèi)只允許調(diào)用一次。

5. 解綁監(jiān)聽事件

解綁事件提前,組件中有設(shè)定監(jiān)聽器,如果該圖表已經(jīng)被加載了泳唠,那么這個(gè)監(jiān)聽器就沒(méi)有作用了狈网。

window.removeEventListener('scroll',  this.scrollEvent)

這段代碼就是解綁監(jiān)聽器,多多少少會(huì)優(yōu)化一下速度吧笨腥。因?yàn)樵黾颖O(jiān)聽和解綁監(jiān)聽的時(shí)間函數(shù)要求一致拓哺,所以在data中新增了scrollEvent,順便把節(jié)流函數(shù)一起加了上去脖母。

6. 請(qǐng)求異步控制setOption

由于用于渲染 echarts 的數(shù)據(jù)常常是異步獲取的士鸥,也就是說(shuō),option 可能會(huì)在異步調(diào)用結(jié)束之后更新谆级,從而觸發(fā) option 的 watch烤礁,進(jìn)而導(dǎo)致 this.checkOption() 執(zhí)行讼积,最終使得 setOption 在頁(yè)面沒(méi)有滾動(dòng)到合適位置時(shí)就觸發(fā)了。
為了解決這個(gè)問(wèn)題脚仔,我們應(yīng)該讓 setOption 的過(guò)程受制于一個(gè)標(biāo)識(shí)位勤众,而該標(biāo)識(shí)位會(huì)在頁(yè)面滾動(dòng)到合適位置時(shí)置為 true,從而杜絕由于 option 更新鲤脏、觸發(fā) watch 而導(dǎo)致的漏洞们颜。
首先,我們要添加一個(gè)新的 data猎醇,取名為為 isPositionReady窥突,然后,在 checkAndSetOption() 中加入對(duì)該標(biāo)識(shí)位的判斷:最后硫嘶,在位置檢測(cè)方法 checkPosition() 中阻问,當(dāng)達(dá)到合適位置時(shí),將該標(biāo)識(shí)位置為 true

    checkPosition() {
     // ....
      if(windowBottom >= selfTop) {
        this.isPositionReady = true
        // ....
      }
    },
    checkAndSetOption() {
    // ....
      if(!this.isPositionReady) return 
      //....
    }

四音半、echarts重繪

這里的重繪指的是 ehcarts 中的 resize() 方法则拷。用于在某些時(shí)刻進(jìn)行 echarts 的調(diào)整,包括:

  • 組件寬度設(shè)置為百分比曹鸠,瀏覽器寬度發(fā)生變化時(shí)煌茬;
  • 頁(yè)面收縮元素狀態(tài)改變,如側(cè)邊欄收縮導(dǎo)致內(nèi)容區(qū)寬度變化彻桃;

1.頁(yè)面寬度改變

繼續(xù)增加監(jiān)聽器就能完成

window.addEventListener('resize', _.throttle(() => {
    this.myEcharts.resize()
    console.log('---')
}, 500))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坛善,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子邻眷,更是在濱河造成了極大的恐慌眠屎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肆饶,死亡現(xiàn)場(chǎng)離奇詭異改衩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)驯镊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門葫督,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人板惑,你說(shuō)我怎么就攤上這事橄镜。” “怎么了冯乘?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵洽胶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我裆馒,道長(zhǎng)姊氓,這世上最難降的妖魔是什么丐怯? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮他膳,結(jié)果婚禮上响逢,老公的妹妹穿的比我還像新娘。我一直安慰自己棕孙,他們只是感情好舔亭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蟀俊,像睡著了一般钦铺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肢预,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天矛洞,我揣著相機(jī)與錄音,去河邊找鬼烫映。 笑死沼本,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锭沟。 我是一名探鬼主播抽兆,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼族淮!你這毒婦竟也來(lái)了辫红?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤祝辣,失蹤者是張志新(化名)和其女友劉穎贴妻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蝙斜,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡名惩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了孕荠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绢片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖岛琼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巢株,我是刑警寧澤槐瑞,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站阁苞,受9級(jí)特大地震影響困檩,放射性物質(zhì)發(fā)生泄漏祠挫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一等舔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧糟趾,春花似錦慌植、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至非驮,卻和暖如春交汤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劫笙。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工芙扎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人填大。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓戒洼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親栋盹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子施逾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容