龙空技术网

从Echarts报错,来学习Vue3中的ref和shallowRef区别

水冗水孚 41

前言:

现时姐妹们对“echarts 清空数据 setoptions报错”都比较关心,兄弟们都需要分析一些“echarts 清空数据 setoptions报错”的相关资讯。那么小编在网摘上网罗了一些对于“echarts 清空数据 setoptions报错””的相关资讯,希望咱们能喜欢,你们一起来了解一下吧!

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的时候报错了报错截图如下:报错截图

添加图片注释,不超过 140 字(可选)

报错原因分析报错的原因是Echarts初始化的实例变量受到了Vue响应式ref的影响——啥意思呢?就是响应式的原理就是代理,也就是说,通过ref函数加工代理的Echarts实例,已经不是原来的实例了。通俗而言,就是ref函数“克隆”了一份Echarts本体实例,本体实例自带resize方法,但是代理克隆体上的resize方法可能克隆的不太完美【这样描述不太严谨,反正是这个意思】也就可能导致了ref函数克隆体的Echarts实例在调用时出错解决方案既然Echarts初始化的实例变量会受到Vue响应式的影响那么我们在存储Echarts的时候,就不存到Vue的响应式变量里面即可如下:

思考,难道所有的变量,都要,都得,都必须通过ref或者reactive定义成响应式的吗?

原来的写法用ref存储:不建议

import * as echarts from "echarts";const eChaDom = ref(null); // 用于初始化Echarts画布需要的dom元素const chart = ref(null) // 用于存储Echartschart.value = echarts.init(eChaDom.value) // 初始化实例
解决方案一:直接使用普通变量来存储Echarts实例
import * as echarts from "echarts";let eChaDom = document.querySelector('.eChaDom'); // 用于初始化Echarts画布需要的dom元素let chart = null // 用于存储Echartschart = echarts.init(eChaDom) // 初始化实例
解决方案二:使用浅层响应式shallowRef进行存储Echarts实例我们知道ref响应式有些过头了,稍微一变都能感应到,而Echart的实例是不可变的你变我不变,就容易打架出问题所以,若是不想使用普通变量来存储Echarts实例,使用shallowRef进行定义存储也是可以的如下:
import * as echarts from "echarts";const eChaDom = shallowRef(null); // 用于初始化Echarts画布需要的dom元素const chart = shallowRef(null) // 用于存储Echartschart.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) // 用于存储Echartschart.value = markRaw(echarts.init(eChaDom.value)) // 初始化实例
这种方式有些多此一举了,本来ref就是要代理克隆的一份,我们再使用markRaw去强制不允许代理克隆一份这种方式不太推荐属于奇葩的操作思考ref和shallowRef应用场景————性能优化我们平常定义一个变量,可以将其定义成响应式的,或者非响应式的定义成响应式的是为了后续改它,自动触发页面视图更新可是啊,在Echarts中,初始化的实例一般也不用去更改,更多的是去调用其自带的方法所以我们没必要还用ref将其定义响应式存储直接定义一个非响应式数据去存储一下也没问题的当然,折中一下,就是用浅层响应式的shallowRef来定义存储吧

实际上,这也是性能优化提升的一种方式

因为ref是把一个变量递归深层次加工成响应式【耗时不少】,而shallowRef操作加工【耗时少】

我们看官方的shallowRef和markRaw这两张图,就能够理解明白了:

图:

添加图片注释,不超过 140 字(可选)

图:

添加图片注释,不超过 140 字(可选)

官方地址在这:

总结响应式变量有对应的好处、非响应式变量也有其优点我们应该根据实际情况,去灵活定义一个变量到底是响应式还是非响应式的【亦或是浅层响应式的】本文就是一个实际情况【shallowRef折中定义一个浅层响应式的Echarts实例的变量】时间允许下,可以多研究研究一些报错的具体原因,这样可以加深我们对于技术的理解

当然,github就这个问题,也有对应的issue。地址在这里:

一句话总结,某些大一些的、不需要更改的实例化的数据对象,就不需使用ref定义成深层响应式啦(直接用普通变量存储也无妨)。若是依旧想定义成响应式的,那就使用shallowRef即可一句话总结,某些大一些的、不需要更改的实例化的数据对象,就不需使用ref定义成深层响应式啦直接用普通变量存储也无妨若是依旧想定义成响应式的,那就使用shallowRef即可

嗯,这样记,通俗易懂

封装的Echarts组件,可复现对应报错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        }    ]}</script><style>.tenBox { width: 100%; height: 90vh; }</style>

标签: #echarts 清空数据 setoptions报错