本文介绍在 Vue 3(尤其是组合式 API)中,如何在父组件挂载后准确获取其 DOM 宽度,并基于该宽度动态计算子组件尺寸(如方格大小),再通过 props 可靠传递给子组件,解决 ref 在 computed 中尚未就绪导致的 undefined 问题。
本文介绍在 vue 3(尤其是组合式 api)中,如何在父组件挂载后准确获取其 dom 宽度,并基于该宽度动态计算子组件尺寸(如方格大小),再通过 props 可靠传递给子组件,解决 `ref` 在 `computed` 中尚未就绪导致的 `undefined` 问题。
在 Vue 3 中,若需让子组件(如 square-component)尺寸严格响应父容器(如 .board)的实际渲染宽度(例如 board.offsetWidth / rows),直接在 computed 中访问 ref 的 DOM 属性是不可行的——因为 computed 在组件初始化时即求值,而此时 ref 所绑定的 DOM 元素尚未挂载,board.value 为 undefined,导致计算失败。
正确做法是:将尺寸值声明为响应式 ref,并在 onMounted 生命周期钩子中,借助 ResizeObserver 监听父容器尺寸变化,动态更新该 ref。ResizeObserver 不仅能捕获初始挂载后的尺寸,还能在窗口缩放、布局变化时自动重算,确保尺寸始终精准。
以下是优化后的父组件代码:
<template> <div ref="board" class="board"> <square-component v-for="i in props.rows * props.cols" :key="i" :square-size="squareSize" /> </div></template><script setup>import { ref, onMounted } from 'vue'import squareComponent from './square-component.vue'const board = ref(null)const squareSize = ref(0) // 初始化为 0,避免子组件接收 undefinedconst props = defineProps({ cols: { type: Number, default: 10 }, rows: { type: Number, default: 10 }})onMounted(() => { // 确保 board.value 已存在且为有效 DOM 元素 if (!board.value) return const resizeObserver = new ResizeObserver(() => { // 动态计算:使用实际 offsetWidth(含边框、内边距),除以行数(或列数,按需调整) squareSize.value = board.value.offsetWidth / props.rows }) resizeObserver.observe(board.value) // 清理:组件卸载时停止观察(可选,但推荐) onUnmounted(() => { resizeObserver.disconnect() })})</script><style scoped>.board { width: 0.5vh; /* 注意:0.5vh 是极小值,确保有可见宽度 */ /* 推荐补充最小宽度或媒体查询,避免极端情况下 width=0 */ min-width: 200px;}</style>
子组件保持简洁,直接消费 squareSize:
立即学习“前端免费学习笔记(深入)”;
<!-- square-component.vue --><template> <div :style="[sizeStyle, myOtherComputedStyles]"></div></template><script setup>import { computed } from 'vue'const props = defineProps({ squareSize: { type: Number, required: true }})const sizeStyle = computed(() => ({ width: `${props.squareSize}px`, height: `${props.squareSize}px`, // 可添加 transition 或 box-sizing: border-box 确保样式稳定}))</script>
关键注意事项:
此方案兼顾准确性、响应性和可维护性,是 Vue 3 中处理“依赖 DOM 尺寸的动态 props”问题的标准实践。