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

相关推荐
白兰地空瓶5 分钟前
🚀 10 分钟吃透 CSS position 定位!从底层原理到避坑实战,搞定所有布局难题
前端·css
T___T17 分钟前
Ajax 数据请求详解与实战
javascript·面试
onthewaying25 分钟前
在Android平台上使用Three.js优雅的加载3D模型
android·前端·three.js
冴羽31 分钟前
能让 GitHub 删除泄露的苹果源码还有 8000 多个相关仓库的 DMCA 是什么?
前端·javascript·react.js
悟能不能悟33 分钟前
jsp怎么拿到url参数
java·前端·javascript
程序猿小蒜1 小时前
基于SpringBoot的企业资产管理系统开发与设计
java·前端·spring boot·后端·spring
Mapmost1 小时前
零代码+三维仿真!实现自然灾害的可视化模拟与精准预警
前端
程序猿_极客1 小时前
JavaScript 的 Web APIs 入门到实战全总结(day7):从数据处理到交互落地的全链路实战(附实战案例代码)
开发语言·前端·javascript·交互·web apis 入门到实战
suzumiyahr1 小时前
用awesome-digital-human-live2d创建属于自己的数字人
前端·人工智能·后端
萧曵 丶1 小时前
Python 字符串、列表、元组、字典、集合常用函数
开发语言·前端·python