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

相关推荐
敲敲了个代码6 小时前
从硬编码到 Schema 推断:前端表单开发的工程化转型
前端·javascript·vue.js·学习·面试·职场和发展·前端框架
dly_blog7 小时前
Vue 响应式陷阱与解决方案(第19节)
前端·javascript·vue.js
消失的旧时光-19437 小时前
401 自动刷新 Token 的完整架构设计(Dio 实战版)
开发语言·前端·javascript
console.log('npc')8 小时前
Table,vue3在父组件调用子组件columns列的方法展示弹窗文件预览效果
前端·javascript·vue.js
用户47949283569158 小时前
React Hooks 的“天条”:为啥绝对不能写在 if 语句里?
前端·react.js
我命由我123458 小时前
SVG - SVG 引入(SVG 概述、SVG 基本使用、SVG 使用 CSS、SVG 使用 JavaScript、SVG 实例实操)
开发语言·前端·javascript·css·学习·ecmascript·学习方法
用户47949283569159 小时前
给客户做私有化部署,我是如何优雅搞定 NPM 依赖管理的?
前端·后端·程序员
C_心欲无痕9 小时前
vue3 - markRaw标记为非响应式对象
前端·javascript·vue.js
qingyun9899 小时前
深度优先遍历:JavaScript递归查找树形数据结构中的节点标签
前端·javascript·数据结构
胡楚昊9 小时前
NSSCTF动调题包通关
开发语言·javascript·算法