Vue 3 中 ref 获取 scrollHeight 属性为 undefined 问题定位

本文共五部分,问题出现场景、定位问题、思考原因、问题解决、总结。 定位到ref获取到Vue 组件实例的代理(Proxy)对象问题。

1. 问题出现场景

vue3使用Splitpanes组件,实现可拖拽分割窗口功能时,需要在Pane组件上,实现滚动到底部效果。

在开发过程中发现,在pane组件上使用ref获取组件属性,获取不到scrollHeight,值为undefined。

html代码如下:

html 复制代码
     <Splitpanes class="flex flex-row h-full default-theme">
        <Pane
          v-for="(item, index) in models"
          ref="scrollRef"
          :key="index"
          :class="[isMobile ? 'p-2' : 'p-4']"
          class="h-full w-1/2 m-auto dark:bg-[#101014] overflow-hidden overflow-y-auto"
        >
          <div id="image-wrapper" class="relative">
            <Card
              :src-url="item.avatar"
              :avatar-type="item.type" :data-sources="modelChat(item.type, +uuid)" :loading="loading"
              @scroll-update="scrollUpdate" @handle-stop="handleStop" @handle-delete="handleDelete"
              @on-regenerate="onRegenerate"
            />
          </div>
        </Pane>
      </Splitpanes>

滑轮滚动到底部js代码如下:

js 复制代码
  const scrollToBottom = async () => {
    // 等待下一个 DOM 更新周期,确保 DOM 已经渲染完成
    await nextTick()
    console.warn('scrollRef:', scrollRef.value)
    // 处理多个滚动容器的情况(数组形式)
    if (scrollRef.value && Array.isArray(scrollRef.value)) {
      scrollRef.value.forEach((item) => {
        // 设置滚动位置为滚动容器的总高度,即滚动到底部
        console.warn('scrollHeight:', item.scrollHeight)
        item.scrollTop = item.scrollHeight
      })
    }
    // 处理单个滚动容器的情况
    else if (scrollRef.value) {
      scrollRef.value.scrollTop = scrollRef.value.scrollHeight
    }
  }

2.定位问题

使用浏览器打印节点,查看属性:

可以看出,ref获取到Vue 组件实例的代理(Proxy)对象,【在 Vue 3 中,组件实例被包装在 Proxy 中以实现响应式】, 每个组件实例上都有一些属性,例如:

  • $i18n:国际化相关的对象
  • $el:组件根 DOM 元素的引用(目前是省略状态,可能需要展开才能看到具体值)
  • $attrs:组件的属性
  • $props:组件的 props
  • $emit:触发事件的方法
  • 以及一些其他 Vue 内部属性和方法

实例上没有scrollHeight属性。

3. 思考原因

scrollHeight 是 DOM 元素的属性,而不是 Vue 组件实例的属性。当你通过 ref 获取第三方组件时,你得到的是组件实例,而不是底层的 DOM 元素。

对比说明:

属性 所属对象 说明
scrollTop 组件实例/ DOM 元素 组件可能暴露了这个属性
scrollHeight DOM 元素 原生 DOM 属性,不会自动暴露

4. 问题解决

对ref获取的节点,取$el属性,获取scrollHeight值,所以修改js代码为:

js 复制代码
  const scrollRef = ref<ComponentPublicInstance>()

  /**
   * 滚动到底部
   * 将滚动条滚动到容器的最底部
   * 适用于聊天场景,当有新消息时自动滚动到底部
   */
  const scrollToBottom = async () => {
    // 等待下一个 DOM 更新周期,确保 DOM 已经渲染完成
    await nextTick()
    // 处理多个滚动容器的情况(数组形式)
    if (scrollRef.value && Array.isArray(scrollRef.value)) {
      scrollRef.value.forEach((item) => {
        // 设置滚动位置为滚动容器的总高度,即滚动到底部
        if (item.$el) {
          item.$el.scrollTop = item.$el.scrollHeight
        }
      })
    }
    // 处理单个滚动容器的情况
    else if (scrollRef.value && scrollRef.value.$el) {
      const documentElement = scrollRef.value.$el
      documentElement.scrollTop = documentElement.scrollHeight
    }
  }

5. 总结

使用vue3提供的方法时,确定函数返回的对象类型很重要。

以上是手动写的hooks函数,也可以使用函数 | VueUse 工具库, vue成熟的hooks库,里面实现的scroll相关函数。

相关推荐
子兮曰2 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
paopaokaka_luck5 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787055 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
IMPYLH5 小时前
HTML 的 <video> 元素
前端·html
溪语流沙6 小时前
【每天一个CSS | Day19】流光文字与一笔写成的手写签名
前端·javascript·css
逐光者9336 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32
To_OC6 小时前
同样是让 AI 写全栈,为什么别人一句话就够了
前端·前端框架·next.js
CDwenhuohuo6 小时前
electron pc项目打包成桌面端
前端·javascript·electron
前端大斗师6 小时前
「大于 1000」把 1000 元那单也算进去了:我在 Vue3 订单页对了 10 句话
前端·人工智能·typescript·大模型·原力计划
Jmyd01237 小时前
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路
前端·3d·vr·三维数字化