Echarts报错"TypeError: Cannot read property 'getAttribute' of null"解决方法

1,369 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

前言
最近在使用 **echarts** 的时候碰到了这么一个报错,
造成报错的原因是因为 **echarts** 的图形容器还未生成就对其进行了初始化,
下面几种方法是经本人自测最有效的解决方案。
错误原因分析:

1、 初始化 echarts 的 DOM 元素不存在。
2、 使用echarts并且在容器上使用了v-if出现这种情况。
3、 html元素还没加载或者代码还没有执行完就先执行了echarts 的渲染,也就是代码执行前后顺序的问题。

解决方案:

第一种方法:
将 created(){} 生命周期中的方法放在 mounted(){} 生命周期中,该方法思路是因为数据渲染方法放到了 created(){} 生命周期中,但是数据还未取到,页面已经加载了,故放在 mounted(){} 生命周期中,在初始化页面完成后,再对 DOM 节点进行相关操作。

mounted() {
    this.myEcharts();
},

第二种方法:
根据报错信息查找原因发现是因为 实例化echarts的元素不存在,查看官方文档代码示例:

// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('main'));

基于准备好的dom,初始化echarts实例,也就是只有 页面存在的HTML元素 才可以去初始化。但是有时候会出现获取不到dom容器的情况,,说白了就是执行顺序的问题,
所以就将获取节点的方法document.getElementById()的方式更换使用vue的ref与$refs来进行获取图表容器节点就行

  <div id="Echarts" ref="Echarts"></div>
 
    mounted() {
      this.myEcharts();
    },
    methods: {
      myEcharts() {
        //var Echarts = document.getElementById('Echarts');
        var Echarts = this.$refs.Echarts;
        // 将之前的方法更换为$refs来进行获取
        // 在进行判断当前节点存在后进行echarts图表数据的填写
 
        if (Echarts){
          console.log('bar_dv不为空');
          let myChart = this.$echarts.init(Echarts)
 
          图表代码 ...
 
        }
      }
    }

正常开发难免调用接口返回数据渲染,可能更改为ref与refs的方法不能完全解决这个问题。这个时候就可以在async await接口之后,这ref与$refs的方法的基础上添加一个定时器来延时执行

  <div id="Echarts" ref="Echarts"></div>
  
    mounted() {
      // 进入页面请求数据
      this.myData();
    },
    methods: {
      async myData(){
          let {code, data, msg } = await myData()
          if(code == 200){
            this.xxx = data
            // 数据请求成功 --- 添加定时器setTimeout 将任务更改为异步
            setTimeout(() => {
                this.myEcharts();
            },500)
          }else{
            return false
          }
      },
      myEcharts() {
        var Echarts = this.$refs.Echarts;
        // 将之前的方法更换为$refs来进行获取
        // 在进行判断当前节点存在后进行echarts图表数据的填写
        if (Echarts){
          console.log('bar_dv不为空');
          let myChart = this.$echarts.init(Echarts)
 
          图表代码 ...
 
        }
      }
    }

第三种方法:
最后的情况就是添加this.$nextTick方法  我们需要 包裹在渲染图表的方法外层等待dom加载完成后执行的钩子 该方法思路是将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。

  <div id="Echarts" ref="Echarts"></div>
 
    mounted() {
      // 进入页面请求数据
      this.myData();
    },
    methods: {
      async myData(){
          let {code, data, msg } = await myData()
          if(code == 200){
            this.xxx = data
            // 数据请求成功 --- 添加定时器setTimeout 将任务更改为异步
            // 添加$nextTick   dom加载完后执行的钩子
            this.$nextTick(() => {
                 setTimeout(() => {
                     this.myEcharts();
                 },500)
            })
          }else{
            return false
          }
      },
      myEcharts() {
        var Echarts = this.$refs.Echarts;
        // 将之前的方法更换为$refs来进行获取
        // 在进行判断当前节点存在后进行echarts图表数据的填写
        if (Echarts){
          console.log('bar_dv不为空');
          let myChart = this.$echarts.init(Echarts)
          
          图表代码 ...
 
        }
      }
    }

第四种方法:
其实解决很简单,只需要将v-if更换成v-show即可。对比v-if和v-show的不同和渲染机制就可以。简单理解一点,深层的dom渲染机制不理解。大佬可以补充

   <div id="Echarts" ref="Echarts" v-if="shipEcharts"></div> 
 
   <div id="Echarts" ref="Echarts" v-show="shipEcharts"></div>

总结

四种方法

  1. 放在 mounted(){} 生命周期中
  2. 使用vue的ref与$refs -- 实际开发async await中可添加一个定时器
  3. 添加this.$nextTick方法
  4. v-if更换成v-show