vue3封裝echarts組件的實(shí)現(xiàn)步驟
1、引言
在現(xiàn)代Web應(yīng)用開發(fā)中,數(shù)據(jù)可視化已成為不可或缺的一部分。ECharts,作為一款強(qiáng)大的圖表庫(kù),提供了豐富的圖表類型和高度定制化的選項(xiàng),深受開發(fā)者喜愛(ài)。然而,在Vue項(xiàng)目中直接使用ECharts可能會(huì)遇到狀態(tài)管理、響應(yīng)式更新和組件化封裝的挑戰(zhàn)。本文將介紹如何在Vue3中封裝一個(gè)高效、可復(fù)用的ECharts組件——TChart
。
2、組件亮點(diǎn)
- 響應(yīng)式圖表:自動(dòng)調(diào)整大小以適應(yīng)容器。
- 空數(shù)據(jù)展示:支持自定義空數(shù)據(jù)狀態(tài)顯示。
- 事件監(jiān)聽(tīng):自動(dòng)綁定和解綁圖表事件。
- 主題切換:動(dòng)態(tài)改變圖表主題。
- 性能優(yōu)化:通過(guò)防抖函數(shù)減少不必要的渲染和資源消耗。
3、技術(shù)棧
- Vue 3: 使用Composition API進(jìn)行狀態(tài)管理和邏輯組織。
- ECharts: 數(shù)據(jù)可視化核心庫(kù)。
- VueUse: 提供
useResizeObserver
等實(shí)用工具函數(shù)。
4、組件結(jié)構(gòu)
TChart
組件的核心在于其模板和腳本部分:
- 模板:包含圖表容器和空數(shù)據(jù)狀態(tài)展示插槽。
- 腳本:
- 初始化圖表并設(shè)置選項(xiàng)。
- 監(jiān)聽(tīng)窗口和圖表容器尺寸變化,實(shí)現(xiàn)響應(yīng)式布局。
- 自動(dòng)綁定和解綁圖表事件。
- 支持動(dòng)態(tài)主題切換和選項(xiàng)更新。
5、實(shí)現(xiàn)步驟
5.1 安裝echarts
npm install echarts
5.2 注冊(cè)echarts
并在 main 文件中注冊(cè)使用
import * as echarts from "echarts" // 引入echarts app.config.globalProperties.$echarts = echarts // 全局使用
5.3 新建TChart組件
~components/TCharts.vue
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart-container" :id="id" ref="echartRef" /> <slot v-if="formatEmpty" name="empty"> <el-empty v-bind="$attrs" :description="description" /> </slot> <slot></slot> </div> </template> <script setup lang="ts" name="TChart"> import { onMounted, getCurrentInstance, ref, watch, nextTick, onBeforeUnmount, markRaw, useAttrs, } from 'vue' import { useResizeObserver } from '@vueuse/core' import { debounce, toLine } from '../../utils' import { computed } from 'vue' const { proxy } = getCurrentInstance() as any const props = defineProps({ options: { type: Object, default: () => ({}), }, id: { type: String, default: () => Math.random().toString(36).substring(2, 8), }, theme: { type: String, default: '', }, isEmpty: { type: [Boolean, Function], default: false, }, description: { type: String, default: '暫無(wú)數(shù)據(jù)', }, }) const echartRef = ref<HTMLDivElement>() const chart = ref() const emits = defineEmits() const events = Object.entries(useAttrs()) // 圖表初始化 const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) setOption(props.options) // 返回chart實(shí)例 emits('chart', chart.value) // 監(jiān)聽(tīng)圖表事件 events.forEach(([key, value]) => { if (key.startsWith('on') && !key.startsWith('onChart')) { const on = toLine(key).substring(3) chart.value.on(on, (...args) => emits(on, ...args)) } }) // 監(jiān)聽(tīng)元素變化 useResizeObserver(echartRef.value, resizeChart) // 如果不想用vueuse,可以使用下邊的方法代替,但組件使用v-show時(shí),不會(huì)觸發(fā)resize事件 // window.addEventListener('resize', resizeChart) } // 重繪圖表函數(shù) const resizeChart = debounce( () => { chart.value?.resize() }, 300, true ) // 設(shè)置圖表函數(shù) const setOption = debounce( async (data) => { if (!chart.value) return chart.value.setOption(data, true, true) await nextTick() resizeChart() }, 300, true ) const formatEmpty = computed(() => { if (typeof props.isEmpty === 'function') { return props.isEmpty(props.options) } return props.isEmpty }) watch( () => props.options, async (nw) => { await nextTick() setOption(nw) }, { deep: true } ) watch( () => props.theme, async () => { chart.value.dispose() renderChart() } ) onMounted(() => { renderChart() }) onBeforeUnmount(() => { // 取消監(jiān)聽(tīng) // window.removeEventListener('resize', resizeChart) // 銷毀echarts實(shí)例 chart.value.dispose() chart.value = null }) </script> <style lang="scss" scoped> .t-chart { position: relative; width: 100%; height: 100%; &-container { width: 100%; height: 100%; } } </style>
utils/index.ts
type Func = (...args: any[]) => any /** * 防抖函數(shù) * @param { Function } func 函數(shù) * @param { Number } delay 防抖時(shí)間 * @param { Boolean } immediate 是否立即執(zhí)行 * @param { Function } resultCallback */ export function debounce( func: Func, delay: number = 500, immediate?: boolean, resultCallback?: Func ) { let timer: null | ReturnType<typeof setTimeout> = null let isInvoke = false const _debounce = function (this: unknown, ...args: any[]) { return new Promise((resolve, reject) => { if (timer) clearTimeout(timer) if (immediate && !isInvoke) { try { const result = func.apply(this, args) if (resultCallback) resultCallback(result) resolve(result) } catch (e) { reject(e) } isInvoke = true } else { timer = setTimeout(() => { try { const result = func.apply(this, args) if (resultCallback) resultCallback(result) resolve(result) } catch (e) { reject(e) } isInvoke = false timer = null }, delay) } }) } _debounce.cancel = function () { if (timer) clearTimeout(timer) isInvoke = false timer = null } return _debounce } /** * 節(jié)流函數(shù) * @param { Function } func * @param { Boolean } interval * @param { Object } options * leading:初始 trailing:結(jié)尾 */ export function throttle( func: Func, interval: number, options = { leading: false, trailing: true } ) { let timer: null | ReturnType<typeof setTimeout> = null let lastTime = 0 const { leading, trailing } = options const _throttle = function (this: unknown, ...args: any[]) { const nowTime = Date.now() if (!lastTime && !leading) lastTime = nowTime const remainTime = interval - (nowTime - lastTime) if (remainTime <= 0) { if (timer) { clearTimeout(timer) timer = null } lastTime = nowTime func.apply(this, args) } if (trailing && !timer) { timer = setTimeout(() => { lastTime = !leading ? 0 : Date.now() timer = null func.apply(this, args) }, remainTime) } } _throttle.cancel = function () { if (timer) clearTimeout(timer) timer = null lastTime = 0 } return _throttle } /** * 駝峰轉(zhuǎn)換下劃線 * @param { String } name */ export function toLine(name: string) { return name.replace(/([A-Z])/g, '_$1').toLowerCase() }
6、使用組件
6.1使用示例
<template> <div> <el-button @click="isShow = !isShow">{{ isShow ? '隱藏' : '顯示' }}</el-button> <el-button @click="addData()">增加數(shù)據(jù)</el-button> <t-chart v-show="isShow" :options="options" style="width: 100%; height: 500px" @click="click" @dblclick="addData()" @mousedown="mousedown" @mousemove="mousemove" @mouseover="mouseover" @mouseout="mouseout" @globalout="globalout" @contextmenu="contextmenu" @chart="chart" /> </div> </template> <script setup lang="ts"> import TChart from '../components/TChart' import { ref } from 'vue' const options = ref({ xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], }, yAxis: { type: 'value', }, series: [ { data: [150, 230, 224, 218, 135, 147, 260], type: 'line', }, ], }) const isShow = ref(true) const addData = () => { options.value.xAxis.data.push( 'test' + Math.random().toString(36).substring(2, 8) ) options.value.series[0].data.push(Math.random() * 200) } const click = (e) => { console.log('click-----', e) } const mousedown = (e) => { console.log('mousedown-----', e) } const mousemove = (e) => { console.log('mousemove-----', e) } const mouseover = (e) => { console.log('mouseover-----', e) } const mouseout = (e) => { console.log('mouseout-----', e) } const globalout = (e) => { console.log('globalout-----', e) } const contextmenu = (e) => { console.log('contextmenu-----', e) } const chartRef = ref(null) const chart = (dom) => { chartRef.value = dom } </script>
6.2效果圖
更多使用示例,可以看看文檔 TChart 圖表
深入淺出---封裝思路
接下來(lái),我想和大家聊一下封裝思路。首先,封裝一個(gè)組件,我需要考慮什么,從哪方面考慮,讓我們看看ai的回答
封裝組件是前端開發(fā)中的一種常見(jiàn)實(shí)踐,它有助于提高代碼的復(fù)用性、可維護(hù)性和可讀性。在進(jìn)行組件封裝時(shí),應(yīng)遵循以下原則:
- 單一職責(zé)原則:一個(gè)組件應(yīng)該只負(fù)責(zé)一項(xiàng)功能,這樣可以保證組件的簡(jiǎn)潔和可復(fù)用性。
- 高內(nèi)聚低耦合:組件內(nèi)部的功能應(yīng)該緊密相關(guān)(高內(nèi)聚),而組件之間的依賴關(guān)系應(yīng)該盡量減少(低耦合)。這有助于提高組件的獨(dú)立性和靈活性。
- 明確的輸入輸出:組件應(yīng)該有清晰的輸入(props)和輸出(state變化或事件)。這有助于其他開發(fā)者理解組件的使用方式。
- 可復(fù)用性:設(shè)計(jì)組件時(shí)應(yīng)考慮到其復(fù)用性,避免硬編碼特定場(chǎng)景的邏輯,使得組件可以在多種場(chǎng)景下使用。
- 可測(cè)試性:組件應(yīng)當(dāng)易于測(cè)試,可以通過(guò)單元測(cè)試和集成測(cè)試驗(yàn)證其功能。避免在組件中使用過(guò)多的全局狀態(tài)或復(fù)雜的異步操作,以降低測(cè)試難度。
- 響應(yīng)式設(shè)計(jì):組件應(yīng)該能夠根據(jù)屏幕尺寸和其他環(huán)境因素自動(dòng)調(diào)整布局和樣式,提供良好的用戶體驗(yàn)。
- 文檔和注釋:為組件編寫詳細(xì)的文檔和注釋,說(shuō)明組件的用途、輸入?yún)?shù)、返回值和使用示例,幫助其他開發(fā)者快速理解和使用組件。
- 性能優(yōu)化:在可能的情況下,對(duì)組件進(jìn)行性能優(yōu)化,如使用虛擬DOM、避免不必要的重渲染等,提高應(yīng)用的運(yùn)行效率。
- 一致性:保持組件風(fēng)格和行為的一致性,遵循項(xiàng)目的編碼規(guī)范和設(shè)計(jì)指南,提高整體的代碼質(zhì)量和用戶體驗(yàn)。
- 模塊化:將大型組件分解成多個(gè)小的、可組合的子組件,每個(gè)子組件負(fù)責(zé)一部分功能,通過(guò)組合這些子組件來(lái)構(gòu)建更復(fù)雜的組件。
遵循這些原則可以幫助你創(chuàng)建出高質(zhì)量、可維護(hù)的組件,從而提升整個(gè)項(xiàng)目的開發(fā)效率和代碼質(zhì)量。
基于上邊的原則,我們?cè)倏紤]下封裝echarts的實(shí)現(xiàn)。
接下來(lái),我們來(lái)看一下官方的示例
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>ECharts</title> <!-- 引入剛剛下載的 ECharts 文件 --> <script src="echarts.js"></script> </head> <body> <!-- 為 ECharts 準(zhǔn)備一個(gè)定義了寬高的 DOM --> <div id="main" style="width: 600px;height:400px;"></div> <script type="text/javascript"> // 基于準(zhǔn)備好的dom,初始化echarts實(shí)例 var myChart = echarts.init(document.getElementById('main')); // 指定圖表的配置項(xiàng)和數(shù)據(jù) var option = { title: { text: 'ECharts 入門示例' }, tooltip: {}, legend: { data: ['銷量'] }, xAxis: { data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'] }, yAxis: {}, series: [ { name: '銷量', type: 'bar', data: [5, 20, 36, 10, 10, 20] } ] }; // 使用剛指定的配置項(xiàng)和數(shù)據(jù)顯示圖表。 myChart.setOption(option); </script> </body> </html>
實(shí)現(xiàn)的步驟的步驟有哪些?
- 引入echarts
- 定義一個(gè)DOM元素(容器)
- 獲取DOM元素(容器)并初始化echarts實(shí)例
- 指定圖表的配置項(xiàng)和數(shù)據(jù)
- 使用剛指定的配置項(xiàng)和數(shù)據(jù)顯示圖表。
每當(dāng)我想使用echarts組件時(shí),都得經(jīng)過(guò)這五個(gè)步驟。當(dāng)我想實(shí)現(xiàn)多個(gè)圖表時(shí),這多個(gè)圖表對(duì)比起來(lái),哪些是步驟是變化的?哪些的不變的?
細(xì)心的網(wǎng)友會(huì)發(fā)現(xiàn),其中,變化最多的,是第四個(gè)步驟“圖表的配置項(xiàng)和數(shù)據(jù)”。那我,是不是可以將這些重復(fù)性的操作,封裝到組件里,讓組件替我去完成。
接下來(lái),讓我們來(lái)一步一步實(shí)現(xiàn)代碼
1.基本功能
1.1準(zhǔn)備DOM元素(容器)
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart" :id="id" ref="echartRef" /> </template> <style lang="scss" scoped> .t-chart { width: 100%; height: 100%; } </style>
1.2 獲取容器并初始化echarts實(shí)例
優(yōu)化小技巧:通過(guò)ref獲取dom實(shí)例比document操作獲取dom,性能更好
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart" :id="id" ref="echartRef" /> </template> <script setup lang="ts" name="TChart"> import { onMounted, getCurrentInstance, ref, markRaw } from "vue" const { proxy } = getCurrentInstance() as any const props = defineProps({ options: { type: Object, default: () => ({}) }, id: { type: String, default: () => Math.random().toString(36).substring(2, 8) } }) const echartRef = ref<HTMLDivElement>() const chart = ref() // 圖表初始化 const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value)) } onMounted(() => { renderChart() }) </script> <style lang="scss" scoped> .t-chart { width: 100%; height: 100%; } </style>
1.3 設(shè)置配置項(xiàng)和數(shù)據(jù)
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart" :id="id" ref="echartRef" /> </template> <script setup lang="ts" name="TChart"> import { onMounted, getCurrentInstance, ref, markRaw } from "vue" const { proxy } = getCurrentInstance() as any const props = defineProps({ options: { type: Object, default: () => ({}) }, id: { type: String, default: () => Math.random().toString(36).substring(2, 8) } }) const echartRef = ref<HTMLDivElement>() const chart = ref() // 圖表初始化 const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value)) setOption(props.options) } // 設(shè)置圖表函數(shù) const setOption = data => { chart.value.setOption(data, true, true) chart.value?.resize() } onMounted(() => { renderChart() }) </script> <style lang="scss" scoped> .t-chart { width: 100%; height: 100%; } </style>
2.組件要實(shí)現(xiàn)的功能
很多時(shí)候,封裝封裝組件,并不是一次性就能做到很完美的狀態(tài),而是在使用中, 不斷去優(yōu)化,取改進(jìn)的。比如,在使用中,數(shù)據(jù)更新、頁(yè)面大小變化時(shí),圖表沒(méi)有重新渲染、echart事件沒(méi)有觸發(fā)。這些都是一點(diǎn)點(diǎn)去優(yōu)化改進(jìn)的。記住一個(gè)準(zhǔn)則:“先實(shí)現(xiàn)再優(yōu)化”
- 響應(yīng)式圖表
- 圖表尺寸的自適應(yīng)
- 事件監(jiān)聽(tīng)
- 性能優(yōu)化
- 空數(shù)據(jù)展示
- 插槽
- 主題切換
- 獲取echarts實(shí)例
3.響應(yīng)式圖表
希望數(shù)據(jù)變化時(shí),可以重新繪制圖表
// 重繪圖表函數(shù) const resizeChart = debounce( () => { chart.value?.resize() }, 300, true ) // 設(shè)置圖表函數(shù) const setOption = debounce( async data => { if (!chart.value) return chart.value.setOption(data, true, true) await nextTick() resizeChart() }, 300, true ) const formatEmpty = computed(() => { if (typeof props.isEmpty === "function") { return props.isEmpty(props.options) } return props.isEmpty }) // 監(jiān)聽(tīng)數(shù)據(jù)變化時(shí),重繪 watch( () => props.options, async nw => { await nextTick() setOption(nw) }, { deep: true } )
4.圖表尺寸的自適應(yīng)
希望容器尺寸變化時(shí),圖表能夠自適應(yīng)
筆者這邊使用了vueuse的useResizeObserver,來(lái)實(shí)現(xiàn)對(duì)元素變化的監(jiān)聽(tīng),為什么沒(méi)用resize? 是因?yàn)槠渲杏锌印?/p>
1、window大小變化時(shí),才會(huì)觸發(fā)監(jiān)聽(tīng)
2、使用組件使用v-show的時(shí)候,不會(huì)觸發(fā),可能會(huì)蜷縮在一團(tuán)
import { useResizeObserver } from "@vueuse/core" const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) setOption(props.options) // 監(jiān)聽(tīng)元素變化 useResizeObserver(echartRef.value, resizeChart) // 大小自適應(yīng) // window.addEventListener('resize', resizeChart) } onBeforeUnmount(() => { // 取消監(jiān)聽(tīng) // window.removeEventListener('resize', resizeChart) })
5.事件監(jiān)聽(tīng)
通過(guò)useAttrs,拿到父組件傳過(guò)來(lái)的事件,并批量注冊(cè)emits事件
const events = Object.entries(useAttrs()) // 監(jiān)聽(tīng)圖表事件 events.forEach(([key, value]) => { if (key.startsWith('on') && !key.startsWith('onChart')) { const on = toLine(key).substring(3) chart.value.on(on, (...args) => emits(on, ...args)) } })
6.性能優(yōu)化
- 通過(guò)markRaw,將echarts實(shí)例標(biāo)記為普通對(duì)象,減少響應(yīng)式帶來(lái)的損耗。
- 防抖函數(shù),用于圖表重繪和選項(xiàng)更新,減少不必要的調(diào)用,提高性能。
- 當(dāng)組件被銷毀時(shí),調(diào)用 dispose 方法銷毀實(shí)例,防止可能的內(nèi)存泄漏。
chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) // 重繪圖表函數(shù) const resizeChart = debounce( () => { chart.value?.resize() }, 300, true ) // 設(shè)置圖表函數(shù) const setOption = debounce( async data => { if (!chart.value) return chart.value.setOption(data, true, true) await nextTick() resizeChart() }, 300, true ) onBeforeUnmount(() => { // 銷毀echarts實(shí)例 chart.value.dispose() chart.value = null })
6.空數(shù)據(jù)展示
組件可以通過(guò)isEmpty,來(lái)設(shè)置echarts圖表空狀態(tài),類型可以是Boolean,也可以是個(gè)函數(shù),方便靈活調(diào)用,還可以設(shè)置description,空數(shù)據(jù)時(shí)的展示文字
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart-container" :id="id" ref="echartRef" /> <slot v-if="formatEmpty" name="empty"> <el-empty v-bind="$attrs" :description="description" /> </slot> <slot></slot> </div> </template> <script setup lang="ts" name="TChart"> const props = defineProps({ isEmpty: { type: [Boolean, Function], default: false, }, description: { type: String, default: '暫無(wú)數(shù)據(jù)', }, }) const formatEmpty = computed(() => { if (typeof props.isEmpty === 'function') { return props.isEmpty(props.options) } return props.isEmpty }) ... </script>
7.插槽
可以通過(guò)插槽,在組件內(nèi)增加內(nèi)容,也可以替換空狀態(tài)的內(nèi)容
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart-container" :id="id" ref="echartRef" /> <slot v-if="formatEmpty" name="empty"> <el-empty v-bind="$attrs" :description="description" /> </slot> <slot></slot> </div> </template> <style lang="scss" scoped> .t-chart { position: relative; width: 100%; height: 100%; &-container { width: 100%; height: 100%; } } </style>
8.主題切換
監(jiān)聽(tīng)props的主題,動(dòng)態(tài)切換echarts 主題
const props = defineProps({ theme: { type: String, default: '', } }) // 圖表初始化 const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) // ... } watch( () => props.theme, async () => { chart.value.dispose() renderChart() } )
9.獲取echarts實(shí)例
注冊(cè)了echarts實(shí)例后,將實(shí)例返回給父組件
chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) // 返回chart實(shí)例 emits('chart', chart.value)
完整代碼
具體的,可以回看5.3 新建TChart組件
<template> <div class="t-chart" v-bind="$attrs"> <div v-show="!formatEmpty" class="t-chart-container" :id="id" ref="echartRef" /> <slot v-if="formatEmpty" name="empty"> <el-empty v-bind="$attrs" :description="description" /> </slot> <slot></slot> </div> </template> <script setup lang="ts" name="TChart"> import { onMounted, getCurrentInstance, ref, watch, nextTick, onBeforeUnmount, markRaw, useAttrs, } from 'vue' import { useResizeObserver } from '@vueuse/core' import { debounce, toLine } from '../../utils' import { computed } from 'vue' const { proxy } = getCurrentInstance() as any const props = defineProps({ options: { type: Object, default: () => ({}), }, id: { type: String, default: () => Math.random().toString(36).substring(2, 8), }, theme: { type: String, default: '', }, isEmpty: { type: [Boolean, Function], default: false, }, description: { type: String, default: '暫無(wú)數(shù)據(jù)', }, }) const echartRef = ref<HTMLDivElement>() const chart = ref() const emits = defineEmits() const events = Object.entries(useAttrs()) // 圖表初始化 const renderChart = () => { chart.value = markRaw(proxy.$echarts.init(echartRef.value, props.theme)) setOption(props.options) // 返回chart實(shí)例 emits('chart', chart.value) // 監(jiān)聽(tīng)圖表事件 events.forEach(([key, value]) => { if (key.startsWith('on') && !key.startsWith('onChart')) { const on = toLine(key).substring(3) chart.value.on(on, (...args) => emits(on, ...args)) } }) // 監(jiān)聽(tīng)元素變化 useResizeObserver(echartRef.value, resizeChart) // 大小自適應(yīng) // window.addEventListener('resize', resizeChart) } // 重繪圖表函數(shù) const resizeChart = debounce( () => { chart.value?.resize() }, 300, true ) // 設(shè)置圖表函數(shù) const setOption = debounce( async (data) => { if (!chart.value) return chart.value.setOption(data, true, true) await nextTick() resizeChart() }, 300, true ) const formatEmpty = computed(() => { if (typeof props.isEmpty === 'function') { return props.isEmpty(props.options) } return props.isEmpty }) watch( () => props.options, async (nw) => { await nextTick() setOption(nw) }, { deep: true } ) watch( () => props.theme, async () => { chart.value.dispose() renderChart() } ) onMounted(() => { renderChart() }) onBeforeUnmount(() => { // 取消監(jiān)聽(tīng) // window.removeEventListener('resize', resizeChart) // 銷毀echarts實(shí)例 chart.value.dispose() chart.value = null }) </script> <style lang="scss" scoped> .t-chart { position: relative; width: 100%; height: 100%; &-container { width: 100%; height: 100%; } } </style>
最后看看是否符合組件的設(shè)計(jì)原則
以上,就是我實(shí)現(xiàn)echarts組件的思路。
總結(jié)
到此這篇關(guān)于vue3封裝echarts組件的文章就介紹到這了,更多相關(guān)vue3封裝echarts組件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue實(shí)現(xiàn)上拉加載下一頁(yè)效果的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Vue實(shí)現(xiàn)上拉加載下一頁(yè)效果,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Vue有一定幫助,需要的可以參考一下2022-08-08如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁(yè)數(shù)據(jù)的組件
這篇文章主要介紹了如何封裝了一個(gè)vue移動(dòng)端下拉加載下一頁(yè)數(shù)據(jù)的組件,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01詳解vue之自行實(shí)現(xiàn)派發(fā)與廣播(dispatch與broadcast)
這篇文章主要介紹了詳解vue之自行實(shí)現(xiàn)派發(fā)與廣播(dispatch與broadcast),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01el-table樹形數(shù)據(jù)量過(guò)大,導(dǎo)致頁(yè)面卡頓問(wèn)題及解決
這篇文章主要介紹了el-table樹形數(shù)據(jù)量過(guò)大,導(dǎo)致頁(yè)面卡頓問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04vue實(shí)現(xiàn)導(dǎo)出excel的多種方式總結(jié)
在Vue中實(shí)現(xiàn)導(dǎo)出Excel有多種方式,可以通過(guò)前端實(shí)現(xiàn),也可以通過(guò)前后端配合實(shí)現(xiàn),這篇文章將為大家詳細(xì)介紹幾種常用的實(shí)現(xiàn)方式,需要的可以參考下2023-08-08