vue3 + Echarts 报 Cannot read properties of undefined (reading 'type') 错误
...大约 2 分钟
vue3 + Echarts 报 Cannot read properties of undefined (reading 'type') 错误
背景
使用 vue3 + Echarts,在进行页面监听,重新渲染 Echarts 的时候,出现 Cannot read properties of undefined (reading 'type') 错误。
由于之前 vue2 中会在 data 中定义 Echarts 实例,并且通过 this.echarts.xxx() 来调用 API 并未出现异常,所以在 Vue3 惯性使用 ref 定义实例。但是 Vue3 使用了 proxy,Echarts 无法从中获取内部变量
一、解决方法一,使用 shallowRef
<template>
<div :id="uid" class="charts-box"></div>
</template>
<script setup>
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import * as echarts from 'echarts'
const props = defineProps({
dataSource: {
type: Object,
default: null,
required: true
},
canvasWidth: {
type: String,
default: '',
required: true
},
canvasHeight: {
type: String,
default: '',
required: true
}
})
const uid = ref('')
const myChart = shallowRef() // 使用 shallowRef 定义实例
// 时间戳+随机字符
uid.value = new Date().getTime() + Math.random().toString(32).slice(2, 10)
console.log('uid:', uid.value)
let style = computed(() => ({
width: props.canvasWidth,
height: props.canvasHeight
}))
const init = () => {
// 基于准备好的dom,初始化echarts实例
myChart.value = echarts.init(document.getElementById(uid.value))
// 绘制图表
myChart.value.setOption(props.dataSource)
try {
window.removeEventListener('resize', resizeChart, true)
} catch (error) {
console.log('error', error)
}
window.addEventListener('resize', resizeChart, true)
}
const resizeChart = () => {
console.log(56)
myChart.value.resize()
}
onMounted(() => {
init()
})
</script>
<style scoped>
.charts-box {
width: 100%;
height: 100%;
}
</style>
提示
Ref 可以给数据添加响应式这个众所周知,获取数据时要多在其后面添加一个 .value
ref 创建的对象默认是响应式的。这意味着当你改变 myChart 的内容时,Vue 会自动追踪这个对象并触发视图的更新。然而,Echarts 实例本身是一个复杂的对象,它并不需要或不适应 Vue 的响应式系统,因为 Vue 无法有效地追踪和更新 Echarts 实例的状态。
shallowRef 和 ref() 不同,浅层 ref 的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对 .value 的访问是响应式的。
使用 shallowRef 的目的是用于对大型数据结构的性能优化或是与外部的状态管理系统集成。
二、解决方法二,使用普通变量储存 Echarts 实例
onMounted(() => {
// 注:图表不能放进data,vue3使用proxy,echarts无法从中获取变量
let charts = [
{ id: 'yearChart', chart: null, options: yearOption },
{ id: 'monthChart', chart: null, options: monthOption }
]
// 使用刚指定的配置项和数据显示图表。
this.$nextTick(() => {
this.charts.forEach(p => {
p.chart = echarts.init(document.getElementById(p.id));
p.chart.setOption(p.options);
})
})
// 监听窗口变化,重绘图表
window.addEventListener("resize", (() => {
this.charts.forEach(p => {
setTimeout(() => { // 避免多图同时加载卡顿
p.chart.resize();
}, 200)
})
}));
}),