Vue监听resize后执行函数,如何确保组件重渲染完成且refs可用?
问题根因
你遇到的偶发refs hasn't rendered yet报错,绝大多数场景是因为没有在组件卸载时移除resize事件监听器:组件销毁后$refs已经被清空,但绑定在window上的resize事件还在触发,此时调用modifyTargetDiv自然拿不到ref。$nextTick本身的作用就是等待当前队列所有DOM更新完成后执行回调,只要组件存活、ref绑定的元素没有被v-if销毁,$nextTick回调里必然能拿到正确的ref,本身不存在问题。
解决方案
方案1:修复事件解绑逻辑(适配所有Vue版本,覆盖90%场景)
添加组件卸载钩子移除监听器,同时搭配防抖优化resize高频触发的性能问题:
<template> <div ref="target"> Hello world </div> </template> <script> export default { mounted() { // 加200ms防抖,避免resize高频触发导致不必要的性能开销 this.debounceModify = this.debounce(this.modifyTargetDiv, 200) window.addEventListener('resize', this.debounceModify); }, // Vue2用beforeDestroy钩子,Vue3用beforeUnmount钩子 beforeDestroy() { window.removeEventListener('resize', this.debounceModify); }, methods: { debounce(fn, delay) { let timer = null return function() { clearTimeout(timer) timer = setTimeout(() => fn.apply(this, arguments), delay) } }, modifyTargetDiv() { this.$nextTick(() => { console.log('refs rendered'); // 对div执行相关操作 // this.$refs.target.getBoundingClientRect() }); } } } </script>
方案2:用ResizeObserver监听元素自身尺寸(更精准,无全局事件污染)
如果你的操作只和目标div自身的尺寸变化相关,不需要监听整个视口的resize,用ResizeObserver更适配需求:
<template> <div ref="target"> Hello world </div> </template> <script> export default { mounted() { // 初始化ResizeObserver,直接监听目标元素尺寸变化 this.resizeObserver = new ResizeObserver(() => { this.$nextTick(() => { console.log('元素尺寸已更新', this.$refs.target) }) }) this.resizeObserver.observe(this.$refs.target) }, // Vue2用beforeDestroy钩子,Vue3用beforeUnmount钩子 beforeDestroy() { // 组件卸载时断开监听,无内存泄漏风险 this.resizeObserver.disconnect() } } </script>
额外注意事项
- 如果你的目标div使用
v-if控制显隐,建议替换为v-show,避免元素被销毁导致ref丢失;如果必须用v-if,可以监听v-if绑定的响应式变量,配置flush: 'post'在DOM更新后执行操作。 - Vue3场景下可以将模板ref绑定为响应式变量,直接监听ref的变化,只要ref不为空时再执行操作,更符合组合式API的开发习惯。
内容的提问来源于stack exchange,提问作者Donut
相关产品推荐
相关产品推荐

