You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

vue-chartjs中$refs引用chart赋值后调用update触发栈溢出原因探究

Vue3中赋值$refs的Chart实例到组件属性后调用update栈溢出的原因

核心原因:Vue响应式Proxy与Chart.js内部遍历的冲突

直接调用this.$refs.myChart.chart.update()正常,赋值给this.chart后调用就栈溢出,本质是响应式代理导致的无限递归:

  1. $refs的非响应性
    Vue3中,组件的$refs是普通对象,不是响应式Proxy。this.$refs.myChart是原始的vue-chartjs组件实例,它的chart属性就是Chart.js的原始实例,没有被Vue的响应式系统包装。调用它的update()时,Chart.js内部的属性访问不会触发任何Vue的拦截逻辑,执行流程正常。

  2. 组件属性的自动响应式包装
    如果你的this.chart是在组件的data()中声明的,或者用ref()/reactive()创建的,当你把this.$refs.myChart.chart赋值给它时,Vue会自动将这个Chart实例包装成响应式Proxy。

  3. Chart.js的内部遍历触发无限递归
    Chart.js的update()方法执行时,会深度遍历自身的配置、数据、上下文等内部对象,进行大量的属性读取操作。而被Proxy包装后的Chart实例,每一次属性读取都会触发Vue的getter拦截器,这个拦截器在处理过程中可能又会访问到Chart实例的其他属性,进而再次触发getter,形成无限递归调用,最终超出JavaScript的调用栈限制,抛出栈溢出错误。

解决办法

根据你的需求选择:

  • 不需要响应式的情况:不在data()/ref()中声明chart,直接在组件实例上挂载普通属性:

    // 比如在created或mounted中
    this.chart = this.$refs.myChart.chart
    

    这样Vue不会对这个属性进行响应式包装,调用this.chart.update()就和直接调用$refs的方式一致。

  • 需要保留响应式的场景:用markRaw()标记Chart实例为非响应式,避免被Vue包装成Proxy:

    import { markRaw } from 'vue'
    
    // 赋值时
    this.chart = markRaw(this.$refs.myChart.chart)
    

    这样既可以把Chart实例挂载到组件属性上,又不会触发响应式代理带来的递归问题。

内容的提问来源于stack exchange,提问作者rsd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:12:19