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相关函数。

相关推荐
AlienZHOU5 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
Captaincc9 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师10 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen10 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒11 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow11 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81811 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang8007811 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明11 小时前
第 6 章 组件进阶
前端·vue.js
江华森11 小时前
HTTP请求的完整过程详解:从DNS解析到TCP挥手的微秒级实战分析
前端