從Echarts報錯中學習Vue3?ref和shallowRef區(qū)別及其組件二次封裝demo
報錯場景
Uncaught TypeError: Cannot read properties of undefined (reading 'type') at LineView2.render (LineView.js:567:36) echarts.js:976
上述是報錯信息
- 筆者簡單封裝一個Echarts組件,代碼文末附上,使用Vue3搭配Echarts5
- 在初始化echarts.init圖表時,沒有問題,但是當進行自適應resize的時候報錯了
- 報錯截圖如下:
報錯截圖
報錯原因分析
- 報錯的原因是Echarts初始化的實例變量受到了Vue響應式ref的影響——啥意思呢?就是
- 響應式的原理就是代理,也就是說,通過ref函數(shù)加工代理的Echarts實例,已經(jīng)不是原來的實例了。通俗而言,就是ref函數(shù)“克隆”了一份Echarts本體實例,本體實例自帶resize方法,但是代理克隆體上的resize方法可能克隆的不太完美【這樣描述不太嚴謹,反正是這個意思】
- 也就可能導致了ref函數(shù)克隆體的Echarts實例在調(diào)用時出錯
解決方案
- 既然Echarts初始化的實例變量會受到Vue響應式的影響
- 那么我們在存儲Echarts的時候,就不存到Vue的響應式變量里面即可
如下:
思考,難道所有的變量,都要,都得,都必須通過ref或者reactive定義成響應式的嗎?
原來的寫法用ref存儲:不建議
import * as echarts from "echarts"; const eChaDom = ref(null); // 用于初始化Echarts畫布需要的dom元素 const chart = ref(null) // 用于存儲Echarts chart.value = echarts.init(eChaDom.value) // 初始化實例
解決方案一:直接使用普通變量來存儲Echarts實例
import * as echarts from "echarts"; let eChaDom = document.querySelector('.eChaDom'); // 用于初始化Echarts畫布需要的dom元素 let chart = null // 用于存儲Echarts chart = echarts.init(eChaDom) // 初始化實例
解決方案二:使用淺層響應式shallowRef進行存儲Echarts實例
- 我們知道ref響應式有些過頭了,稍微一變都能感應到,而Echart的實例是不可變的
- 你變我不變,就容易打架出問題
- 所以,若是不想使用普通變量來存儲Echarts實例,使用shallowRef進行定義存儲也是可以的
- 如下:
import * as echarts from "echarts"; const eChaDom = shallowRef(null); // 用于初始化Echarts畫布需要的dom元素 const chart = shallowRef(null) // 用于存儲Echarts chart.value = echarts.init(eChaDom.value) // 初始化實例
解決方案三:依舊用ref但是搭配markRaw強制返回自身,不讓代理克隆一份
import { ref, markRaw, shallowRef } from "vue"; import * as echarts from "echarts"; const eChaDom = ref(null); // 用于初始化Echarts畫布需要的dom元素 const chart = ref(null) // 用于存儲Echarts chart.value = markRaw(echarts.init(eChaDom.value)) // 初始化實例
- 這種方式有些多此一舉了,本來ref就是要代理克隆的一份,我們再使用markRaw去強制不允許代理克隆一份
- 這種方式不太推薦
- 屬于奇葩的操作
思考ref和shallowRef應用場景————性能優(yōu)化
- 我們平常定義一個變量,可以將其定義成響應式的,或者非響應式的
- 定義成響應式的是為了后續(xù)改它,自動觸發(fā)頁面視圖更新
- 可是啊,在Echarts中,初始化的實例一般也不用去更改,更多的是去調(diào)用其自帶的方法
- 所以我們沒必要還用ref將其定義響應式存儲
- 直接定義一個非響應式數(shù)據(jù)去存儲一下也沒問題的
- 當然,折中一下,就是用淺層響應式的shallowRef來定義存儲吧
實際上,這也是性能優(yōu)化提升的一種方式
因為ref是把一個變量遞歸深層次加工成響應式【耗時不少】,而shallowRef操作加工【耗時少】
我們看官方的shallowRef和markRaw這兩張圖,就能夠理解明白了:
圖:
圖:
小結
- 響應式變量有對應的好處、非響應式變量也有其優(yōu)點
- 我們應該根據(jù)實際情況,去靈活定義一個變量到底是響應式還是非響應式的【亦或是淺層響應式的】
- 本文就是一個實際情況【shallowRef折中定義一個淺層響應式的Echarts實例的變量】
- 時間允許下,可以多研究研究一些報錯的具體原因,這樣可以加深我們對于技術的理解
當然,github就這個問題,也有對應的issue。地址在這里
一句話總結,某些大一些的、不需要更改的實例化的數(shù)據(jù)對象,就不需使用ref定義成深層響應式啦(直接用普通變量存儲也無妨)。若是依舊想定義成響應式的,那就使用shallowRef即可
- 一句話總結,某些大一些的、不需要更改的實例化的數(shù)據(jù)對象,就不需使用ref定義成深層響應式啦
- 直接用普通變量存儲也無妨
- 若是依舊想定義成響應式的,那就使用shallowRef即可
嗯,這樣記,通俗易懂
封裝的Echarts組件,可復現(xiàn)對應報錯bug
組件二次封裝Echarts代碼
<template> <div ref="eChaDom" :style="{ height: h }" /> </template> <script setup> import { watch, onMounted, onBeforeUnmount, ref, shallowRef } from "vue"; import * as echarts from "echarts"; import debounce from 'lodash/debounce' const props = defineProps({ h: { type: String, default: '360px' }, options: { type: Object, default: () => ({}) }, theme: { type: String, default: 'dark' } }) // const eChaDom = ref(null); // 這樣resize有報錯 // const chart = ref(null) const eChaDom = shallowRef(null); // 這樣resize就沒報錯了 const chart = shallowRef(null) const init = () => { chart.value = echarts.init(eChaDom.value, props.theme) chart.value.setOption(props.options); window.addEventListener('resize', debounce(resizeFn, 360)) } const resizeFn = () => { chart.value.resize() } onMounted(() => { init() }) watch( () => props.options, (newOptions) => { chart.value.setOption(newOptions); }, { deep: true } ) onBeforeUnmount(() => { window.removeEventListener('resize', resizeFn) }) </script>
使用組件
<template> <div class="tenBox"> <eCha :options="options" h="600px" /> </div> </template> <script setup> import eCha from "@/components/eCha/index.vue"; const options = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [ { data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', smooth: true } ] }
以上就是從Echarts報錯中學習Vue3 ref和shallowRef區(qū)別及其組件二次封裝demo的詳細內(nèi)容,更多關于Vue3 ref shallowRef區(qū)別的資料請關注腳本之家其它相關文章!
相關文章
Vue?CompositionAPI中watch和watchEffect的區(qū)別詳解
這篇文章主要為大家詳細介紹了Vue?CompositionAPI中watch和watchEffect的區(qū)別,文中的示例代碼簡潔易懂,希望對大家學習Vue有一定的幫助2023-06-06解決vue3.0運行項目warning Insert `·` prettier/pret
這篇文章主要介紹了解決vue3.0運行項目warning Insert `·` prettier/prettier問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10