【关于ResizeObserver在vue中热重载$refs报错等问题】

  • Post author:
  • Post category:vue




报错代码

一般在mounted生命周期钩子中创建

ResizeObserver

实例

mounted(){
  const container = this.$refs.container;
  let containerObserver = new ResizeObserver((entries) => {
      for (let entry of entries) {
          //使用$refs获取container的宽度并输出
          console.log(this.$refs.container.clientWidth)
      }
  });
  containerObserver.observe(container);
}

代码在第一次加载的时候没有任何问题,但是你修改代码并保存,在vue-cli会启动热重载,这时候大概率会出现一下报错:


Uncaught TypeError: Cannot read properties of undefined (reading ‘clientWidth’)

说找不到clientWidth这个属性,但是~~~报错的后面后紧接着出现了container的宽度值,也就是说代码运行是没有任何问题的,就是之只要保存一次,红色错误代码就会出现一遍,这样在开发中也是很烦躁的。



出现原因分析

如果是将代码改成

console.log(this.$refs.container)

你会发现,热重载之后,先会出现一个

undefine

,之后再出现vm的说有

$refs

。也就是说,我只用了一遍输出,但是却执行了两遍代码,斌且第一次是

undefine

,就是说某种dom元素还没有挂载的情况下执行了一遍,之后又执行了一遍…

一下方法是我所尝试的方法…

都无效!!

  • 使用

    $nextTick()


    想到DOM找不到出现

    undefine

    ,就想到使用

    $nextTick()

    在DOM元素都挂载的时候在执行函数。

    mounted(){
        //添加了nextTick()
      this.nextTick(()=>{
          const container = this.$refs.container;
          let containerObserver = new ResizeObserver((entries) => {
              for (let entry of entries) {
                  //使用$refs获取container的宽度并输出
                  console.log(this.$refs.container.clientWidth)
              }
          });
          containerObserver.observe(container);
      })
    }
    

    这个方法在热重载之后还是会出现问题,甚至多了一个nextTick的错误

  • 创建实例时考虑到作用域问题,不适用let,使用var和this创建实例,

    也还是不行

    var containerObserver = new resizeObserver(...)
    this.containerObserver = new resizeObserver(...)
    
  • 判断

    ResizeObserver

    实例是否已经存在

    如果执行了两次,是不是重载之后上一个

    ResizeObserver

    还遗留着被执行了??

    mounted(){
        //添加了nextTick()
      if(!this.containerObserver){
        this.nextTick(()=>{
            const container = this.$refs.container;
            this.containerObserver = new ResizeObserver((entries) => {
                for (let entry of entries) {
                    //使用$refs获取container的宽度并输出
                    console.log(this.$refs.container.clientWidth)
                }
            });
            containerObserver.observe(container);
        })
      }
    }
    

    如果实例不存在,就执行。很可惜,还是不行。



问题解决

在折腾了很久之后…解决了,准确的说…AI解决了我的问题。

下面是我和

chatGPT

的几次对话:


  • 问:

    添加了

    nextTick

    之后还是有问题呢?


    答:

    可能是因为在组件的生命周期中,mounted钩子函数可能会被多次调用,导致多次创建Observer实例,进而造成了重复输出。你可以在mounted钩子函数中判断Observer实例是否已经被创建,如果已经被创建,就不再进行重复创建。

    如果还有问题,你可以在beforeDestroy钩子函数中销毁Observer实例,来保证内存的释放。

没错~~它提到了

beforeDestroy

这个生命周期钩子(在组件销毁之前调用),在试了判断重复没用之后,就用了

beforeDestroy

,问题成功的解决了,如下:

mounted(){
    //添加了nextTick()
  const container = this.$refs.container;
  this.containerObserver = new ResizeObserver((entries) => {
      for (let entry of entries) {
          //使用$refs获取container的宽度并输出
          console.log(this.$refs.container.clientWidth)
      }
  });
  containerObserver.observe(container);
}
...
...
beforeDestroy(){
//使用disconnect()方法将containerObserver实例销毁
  this.containerObserver.disconnect(); 
  this.containerObserver = null;
}


具体的原因大致如下:



问:

为什么热重载的时候,mounted可能会被调用多次,导致ResizeObserver实例被调用了多次。


答:

热重载是在修改了代码之后,重新加载页面而不是重启整个应用,所以组件仍然会被保留在内存中,并且会继续执行它的生命周期钩子函数。所以如果你在mounted钩子函数中创建了Observer实例,那么在热重载的时候,这个钩子函数就会被调用多次,导致多次创建Observer实例。



总结

可能这个问题是一个简单的销毁实例就解决的问题,而且就算不解决也不影响代码的执行,但是红色的报错还是让我忍不住想去探究一下。作为刚接触vue不久的小白的我,虽然一直能大致知道问题的所在,但是却怎么也没想到需要在

beforeDestroy

中去解决。折腾了很久,另外也吐槽一下chatGPT,这个问题重复问了很多遍,把问题解决,但是还是感谢他hhh。

总之,虽然是小问题折腾了很久,但也从中学到了更多,更了解vue的dom渲染机制,以及生命周期函数等等…最后希望能帮助到有需要的人。



版权声明:本文为HunterVG原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。