找回密码
 立即注册
首页 业界区 业界 Echarts与Vue3中获取DOM节点可能出现的异常错误 ...

Echarts与Vue3中获取DOM节点可能出现的异常错误

豹筒生 2025-6-4 22:28:32
useTemplateRef 的简单介绍

官方:返回一个浅层 ref,其值将与模板中的具有匹配 ref attribute 的元素或组件同步。
参数匹配机制‌:useTemplateRe的参数需与模板中 ref 属性值必须完全一致
‌响应式变量类型明确‌:返回值是一个 浅层 ref对象,其 .value 直接指向绑定的 DOM 元素或组件实例。
useTemplateRef源码浅析

packages/runtime-core/src/helpers/useTemplateRef.ts 文件中
  1. import { type ShallowRef, readonly, shallowRef } from '@vue/reactivity'
  2. import { getCurrentInstance } from '../component'
  3. import { warn } from '../warning'
  4. import { EMPTY_OBJ } from '@vue/shared'
  5. export function useTemplateRef(key) {
  6.   // 获取当前 Vue 实例对象。
  7.   const i = getCurrentInstance()
  8.   // 创建一个浅层的ref对象r,初始值为null。
  9.   const r = shallowRef(null)
  10.   //如果存在 Vue 实例
  11.   if (i) {
  12.     // i.refs 默认初始化为 EMPTY_OBJ(空对象)首次使用时动态创建新对象‌
  13.     const refs = i.refs === EMPTY_OBJ ? (i.refs = {}) : i.refs
  14.     Object.defineProperty(refs, key, {
  15. <template>
  16.   
  17.    
  18.   
  19. </template>enumerable: true,
  20. <template>
  21.   
  22.    
  23.   
  24. </template>get: () => r.value,
  25. <template>
  26.   
  27.    
  28.   
  29. </template>set: val => (r.value = val),
  30.       })
  31.   }
  32.   return r
  33. }
复制代码
1.获取当前 Vue 实例对象。
2.创建一个浅层的ref对象r,初始值为null。
3.如果存在 Vue 实例,则获取实例上的refs属性(用于存储模板引用)。
4.使用Object.defineProperty对refs对象的key属性进行拦截:
get拦截:返回r.value,即useTemplateRef返回的ref变量的值。
set拦截:将val赋值给r.value,从而将 DOM 元素或组件实例绑定到ref变量上。
5.返回ref对象r。
下面这段代码看看有啥问题
  1. <template>
  2.   
  3.    
  4.   
  5. </template>
复制代码
1.png

实际情况:this.dom.getContext is not a function

报错信息: Uncaught (in promise) TypeError: this.dom.getContext is not a function
01-echarts引入报错.jpg
为啥会报这个错误呢?
原因:因为在初始化echarts的时候,echarts规定只能传入实际的DOM元素。
此时我们传递的是Ref对象,而不是实际的DOM元素
需要传递 divNode.value。
传递一个实际的DOM元素(divNode.value)
  1. const divNode = useTemplateRef("chartNode");
  2. console.log(1111, divNode)
  3. // 初始化图表,传递实际DOM
  4. let chartDom = echarts.init(divNode.value);
  5. onMounted(() => {
  6.   drawCharts()
  7. })
  8. const drawCharts=()=>{
  9.   ...代码爆出不变
  10. }
复制代码
2.png

为啥还会报错:Error: Initialize failed: invalid dom.

原因在于:let chartDom = echarts.init(divNode.value);这一行代码。
此时divNode.value为null。
为啥是null呢?
这个跟获取时机有关:此时还没有完成绑定哈,所以得到的是null。
什么时候可以正常绑定呢?
第1种:在onMounted中肯定是绑定了,此时divNode.value是一个实际的DOM元素了。
第2种:与它同一级的DOM元素已经渲染完成(其实也是在onMounted)
使用useTemplateRef正常渲染图表
  1. <template>
  2.   
  3.    
  4.   
  5. </template>
复制代码
3.png

DOM元素已经渲染完成,useTemplateRef拿到DOM元素
  1. <template>
  2.   
  3.     我是div元素
  4.     <button @click="getNodeHandler">我是按钮,获取div节点</button>
  5.   
  6. </template>
复制代码
4.png

当然我们除了使用 useTemplateRef 来获取DOM元素
还可以使用 ref 来获取DOM元素
使用 ref 来获取DOM元素

使用ref 来获取DOM元素需要注意的点。
通过ref函数创建,并赋值给与模板中同名的变量。
复制代码
错误的获取DOM节点的方式
复制代码
使用 ref 来获取DOM元素,并渲染echarts
  1. <template>
  2.   
  3.    
  4.   
  5. </template>
复制代码
存储的是ECharts实例变成响应式数据出现的问题
  1. <template>
  2.   
  3.    
  4.   
  5. </template>
复制代码

缩放echarts出现:Cannot read properties of undefined (reading 'type')

缩放窗口大小,echarts图表出现报错信息如下:
Cannot read properties of undefined (reading 'type')
原因是:存储的是 ECharts 实例变成了响应式,从而在resize 的时候获取不到。
其实存储 ECharts 实例不应该是一个响应式的数据。
就是一个普通类型的数据就行
解决办法

现在我们知道出现问题的原因。
解决办法就是不让它变成响应式的数据就行。
1:存储的是 ECharts实例不要变成响应式,让其成为普通对象。
2.使用markRaw将它标记为一个对象,使其不会被转换为响应式对象
第1种我们已经使用过了,下面我们演示第2种
Vue3中的markRaw

我的理解是:  ****
用于‌标记一个对象,使其永远不会被转换为响应式对象‌。返回该对象本身。
该对象即使被包裹在 reactive()、ref()、shallowReactive() 等响应式 API 中
也会保持原始状态,不会触发依赖追踪和视图更新‌。
官网的解释是:
将一个对象标记为不可以被转化为代理对象。返回该对象本身。
使用markRaw来解决
  1. [/code][size=3]切换页面的时候echarts实例会自动销毁嘛?[/size]
  2. 不会的。需要手动销毁。
  3. ‌在 Vue 中切换路由(页面)时或刷新页面时,ECharts 实例不会自动销毁。
  4. 需手动调用 dispose() 方法销毁实例‌,否则会导致内存泄漏或二次渲染异常‌。
  5. [size=3]销毁ECharts实例[/size]
  6. [code]let chartDom = echarts.init('echarts容器');
  7. // 销毁实例,避免内存泄漏
  8. onBeforeUnmount(()=>{
  9.   chartDom && chartDom.dispose()
  10. })
  11. <script>
复制代码
解决echarts第二次无法渲染的问题
  1. // 获取存储echarts容器的节点
  2. let chartNode = document.getElementById('chart')
  3. //移除容器上的 _echarts_instance_ 属性
  4. chartNode.removeAttribute('_echarts_instance_')
复制代码
避免多次重复初始 echarts

通过 echarts.getInstanceByDom() 检查是否已存在实例,避免重复初始化‌
  1. <template>
  2.   
  3.    
  4.   
  5. </template><script setup>import { ref } from 'vue'let chartNode  = ref()// echarts.getInstanceByDom的参数是页面中渲染echarts的DOM节点chartInstance = echarts.getInstanceByDom(chartNode.value);// 检查是否已存在实例if (!chartInstance) {  console.log('实例不存在')}else{  console.log('实例已存在')}<script>
复制代码
                                                                                                                       
6.jpeg
                                                微信                                                                                        <template>
  
   
  
</template>本文版权归作者所有,欢迎转载,未经作者同意须保留此段声明,在文章页面明显位置给出原文连接
                <template>
  
   
  
</template>如果文中有什么错误,欢迎指出。以免更多的人被误导。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册