使用keep-alive组件子组件添加的时候获取宽度获取不到

Posted vieber

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用keep-alive组件子组件添加的时候获取宽度获取不到相关的知识,希望对你有一定的参考价值。

问题背景

第一眼看到这个问题,猜想就是获取宽度的时候元素还未完全渲染。导致拿到的是0.
在这里插入图片描述

this.rightWidth是0

可是我是在mounted里面才去获取宽度,可能是keep-alive有关,导致没有渲染就触发了mounted事件,然后那边就更新了。我需要在后面在拿一次高度设置。

一开始想到了用activated取重新设置宽度,但是发现页面会闪一下。
也就是说activated是在页面绘制之后才触发的。

这个时候已经慢了。我们要在绘制之前就要拿到宽度。
或者是渲染的时候立即拿到宽度,重新设置,触发渲染。

一开始想到用nextTick去解决问题。但是不行,还是没有拿到渲染之后的width。

然后看下nextTick渲染原理

vue的数据响应过程包含:数据更改->通知Watcher->更新DOM。然后vue的nextTick是在更新DOM之后增加的microtask微任务,然后这个微任务会在下一次事件循环之前被执行。

常见的microtask有:Promise、MutationObserver、Object.observe(废弃),以及nodejs中的process.nextTick.

队列控制的最佳选择是microtask,而microtask的最佳选择是Promise.但如果当前环境不支持Promise,vue就不得不降级为macrotask(宏任务)来做队列控制了。

在vue2.5的源码中,macrotask降级的方案依次是:setImmediate、MessageChannel、setTimeout.

所以就是说我们的nextTick在UI render前,也就是你能看到渲染元素之前,在dom创建更新好之后。

所以使用nextTick还是未渲染之后的版本。

解决方案

使用setTimeout代替nextTick获取渲染的宽度,这个时候保证肯定是渲染结束了。这个时候获取到的with就是对的了。

mounted() {
    setTimeout(() => {
        this.rightWidth = this.rightElm.getBoundingClientRect().width;
    }, 0);
},

js线程和UI线程执行顺序

js线程会阻塞UI线程。

浏览器在每次宏任务执行后会插入UI渲染,setTimeout会有2次渲染的弊端。

因为宏任务后会UI render,然后执行宏任务setTimeout,这个时候执行完又会 UI render.所以会2次渲染。

而使用了微任务会直接加入宏任务后面,最后才是渲染
在这里插入图片描述

总结

更详细的事件循环算法(尽管与 规范 相比仍然是简化过的):

  • 从 宏任务 队列(例如 “script”)中出队(dequeue)并执行最早的任务。
  • 执行所有 微任务:
  • 当微任务队列非空时:
  • 出队(dequeue)并执行最早的微任务。
  • 执行渲染,如果有。
  • 如果宏任务队列为空,则休眠直到出现宏任务。
  • 转到步骤 1。

以上是关于使用keep-alive组件子组件添加的时候获取宽度获取不到的主要内容,如果未能解决你的问题,请参考以下文章

vue父组件异步获取数据传值给子组件

keep-alive组件缓存

vue之keep-alive的使用

vue中keep-alive路由缓存

keep-alive的使用

vue.js 使用keep-alive设置返回不刷新