Vue/Nuxt中如何优先获取模板DOM宽度并正确传递给子组件
问题原因
你当前的实现存在3个核心问题:
width未在data中声明,不属于响应式变量,赋值后不会触发子组件props更新- 宽度获取逻辑仅放在
updated钩子中,子组件首次渲染时大概率还没完成宽度计算,会拿到undefined触发类型报错 - 没有做ref存在性校验,DOM未完全挂载时访问
$refs.demandContainer.$el会直接抛出异常
解决方案
按照以下步骤修改即可:
- 父组件声明响应式的
width变量,抽离公共宽度获取方法,在合适的生命周期触发计算 - 新增窗口resize监听保证宽度实时更新,同时做好边界校验避免报错
- 子组件可选增加宽度校验逻辑,避免空值渲染异常
父组件修改后代码
<template> <v-col cols="12" sm="12" md="4" ref="demandContainer"> <div class="chart-container p-4 dark-background"> <div class="font-weight-bold">Demand</div> <area-chart v-if="trendsData.length > 0" :data="trendsData" :baseWidth="width" > </area-chart> </div> </v-col> </template> <script> export default { data() { return { // 声明响应式宽度变量,初始值设为0避免子组件接收到undefined width: 0, trendsData: [] } }, mounted() { // 组件挂载后等DOM完全渲染再获取宽度 this.$nextTick(() => { this.getContainerWidth() }) // 监听窗口大小变化,实时更新宽度 window.addEventListener('resize', this.getContainerWidth) }, beforeDestroy() { // 组件销毁前移除监听,避免内存泄漏 window.removeEventListener('resize', this.getContainerWidth) }, updated() { // 组件更新后重新校验宽度 this.$nextTick(() => { this.getContainerWidth() }) }, methods: { getContainerWidth() { // 先判断ref是否存在,避免DOM未渲染时报错 if (!this.$refs.demandContainer) return const newWidth = this.$refs.demandContainer.$el.clientWidth // 仅宽度真实变化时才赋值,减少不必要的组件更新 if (newWidth !== this.width) { this.width = newWidth } }, // 你的trendsData获取方法,拿到数据后额外触发一次宽度计算 async fetchTrendsData() { // 此处替换为你的实际接口请求逻辑 this.trendsData = await request.get('/your/api/path') this.$nextTick(() => { this.getContainerWidth() }) } } } </script>
子组件可选优化
如果仍存在渲染异常,可在子组件增加宽度判断或监听逻辑:
<!-- 模板增加v-if判断,宽度有效时才渲染svg --> <svg v-if="baseWidth > 0" ref="svg-area" :width="baseWidth" :height="baseHeight"> </svg> <script> export default { props: { baseWidth: { type: Number, default: 0 }, baseHeight: { type: Number, default: 300 } }, watch: { // 若宽度变化后需要重绘图表,可在此处添加对应逻辑 baseWidth(newVal) { if (newVal > 0) { // 你的图表重绘逻辑 } } } } </script>
补充说明
如果是Nuxt服务端渲染场景,所有DOM相关操作都放在mounted钩子中执行即可,服务端不会执行该钩子,不会出现DOM找不到的报错。
内容的提问来源于stack exchange,提问作者Soonk
相关产品推荐
相关产品推荐

