news 2026/9/12 21:28:08

Vant `useRect` 组合式函数完全指南:获取元素尺寸与视口相对位置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vant `useRect` 组合式函数完全指南:获取元素尺寸与视口相对位置

VantuseRect组合式函数完全指南:获取元素尺寸与视口相对位置

【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant

useRect@vant/use提供的一个轻量级组合式函数(Composable),用于获取任意 DOM 元素的尺寸,以及该元素相对于浏览器视口(viewport)的位置,其行为等价于原生Element.getBoundingClientRect()。它被广泛用于 Vant 组件库内部(如 Sticky、List、Calendar、DropdownMenu、IndexBar 等组件)以完成布局测量与滚动联动,本文将从基础用法、API 声明、源码实现到组件级实战应用,带你完整掌握这一工具。

功能概述

useRect的核心价值在于:在 Vue 3 的组合式 API 环境下,以统一的接口读取元素的布局信息。它的输入既可以是 DOM 元素本身,也可以是 Vue 的ref引用,内部通过unref自动解包,调用方无需关心传参形态;同时它还额外处理了Window对象与"元素尚未挂载"两种边界情况,避免直接调用getBoundingClientRect时的各种坑。

它与原生 API 的对应关系如下:

useRect(element)等价的原生调用
传入Elementelement.getBoundingClientRect()
传入Ref<Element>unref(ref).getBoundingClientRect()
传入Window基于innerWidth/innerHeight构造的DOMRect
传入undefined或空 ref返回全 0 的DOMRect

安装与引入

@vant/use已经被包含在 Vant 的依赖中,但官方仍然推荐在项目中显式安装该包,以便直接使用这些组合式 API(详见 Composables 文档):

# with npm npm i @vant/use # with yarn yarn add @vant/use # with pnpm pnpm add @vant/use # with Bun bun add @vant/use

安装完成后,从包入口导入即可(useRect由 入口文件 统一导出):

import { useRect } from '@vant/use';

基础用法

传入元素引用(ref)

在模板中给目标元素绑定ref,然后在onMounted生命周期中调用useRect(此时元素已完成挂载,测量才有意义):

<div ref="root" />
import { ref, onMounted } from 'vue'; import { useRect } from '@vant/use'; export default { setup() { const root = ref(); onMounted(() => { const rect = useRect(root); console.log(rect); // -> the size of an element and its position relative to the viewport }); return { root }; }, };

传入原始 DOM 元素

也可以直接传入元素实例,这在事件回调、指令或第三方库集成场景中很常见:

import { useRect } from '@vant/use'; const el = document.querySelector('.my-element'); const rect = useRect(el);

传入 Window 对象

useRect也接受Window,此时返回的是整个窗口的尺寸(topleft恒为 0):

import { useRect } from '@vant/use'; const rect = useRect(window); // rect.width === window.innerWidth // rect.height === window.innerHeight

API

类型声明

function useRect( element: Element | Window | Ref<Element | Window | undefined>, ): DOMRect;

参数element支持三种形态:

  • Element:直接传入 DOM 元素;
  • Window:传入全局window对象;
  • Ref<Element | Window | undefined>:Vue 的 ref 引用,元素可能暂未挂载(值为undefined)。

返回值统一为标准的DOMRect对象。

返回值

名称说明类型
width元素的宽度number
height元素的高度number
top元素顶部到视口顶部的距离number
left元素左侧到视口左侧的距离number
right元素右侧到视口左侧的距离(即left + widthnumber
bottom元素底部到视口顶部的距离(即top + heightnumber

注意:top/left表示的是元素边框到视口边缘的距离,而right/bottom是到视口左上角的绝对坐标,并非"元素到视口右边/下边的距离"。这与原生getBoundingClientRect()的语义完全一致,页面发生滚动时这些值会随之变化。

源码实现深度解析

useRect的实现非常精简,完整源码位于 packages/vant-use/src/useRect/index.ts。理解它的三个分支,有助于你在边界场景下正确使用:

import { Ref, unref } from 'vue'; const isWindow = (val: unknown): val is Window => val === window; const makeDOMRect = (width: number, height: number) => ({ top: 0, left: 0, right: width, bottom: height, width, height, }) as DOMRect; export const useRect = ( elementOrRef: Element | Window | Ref<Element | Window | undefined>, ) => { const element = unref(elementOrRef); if (isWindow(element)) { const width = element.innerWidth; const height = element.innerHeight; return makeDOMRect(width, height); } if (element?.getBoundingClientRect) { return element.getBoundingClientRect(); } return makeDOMRect(0, 0); };

1.unref自动解包 ref

首行调用 Vue 的unref:如果传入的是 ref,则取其.value;如果传入的是普通元素或Window,则原样返回。这正是"传元素或传 ref 皆可"这一便利性的来源。

2.Window特殊分支

isWindow通过val === window严格判断。由于window对象自身没有getBoundingClientRect方法,如果直接调用会抛出TypeError,因此源码用innerWidth/innerHeight构造了一个DOMRect:此时topleft为 0,right等于窗口宽度,bottom等于窗口高度。

3.getBoundingClientRect主路径

对于普通元素,直接返回element.getBoundingClientRect()的结果,确保返回值与原生 API 完全一致(包含xy等全部标准字段)。

4. 全零兜底

当传入undefined(例如 ref 尚未绑定到已挂载元素,或元素被销毁)时,返回一个widthheighttopleftrightbottom全部为 0 的DOMRect,保证调用方无需做空值判断,也不会因调用不存在的方法而抛错。

在 Vant 组件中的真实应用

useRect不是孤立存在的工具函数,Vant 大量组件都基于它完成布局测量。以下是两个具有代表性的应用场景。

场景一:Sticky 吸顶组件的定位判断

在 Sticky 组件 中,滚动事件回调onScroll(Sticky.tsx)通过useRect同时测量根元素与容器元素的位置,决定是否进入fixed吸顶状态:

const onScroll = () => { if (!root.value || isHidden(root)) { return; } const { container, position } = props; const rootRect = useRect(root); const scrollTop = getScrollTop(window); state.width = rootRect.width; state.height = rootRect.height; if (position === 'top') { if (container) { const containerRect = useRect(container); const difference = containerRect.bottom - offset.value - state.height; state.fixed = offset.value > rootRect.top && containerRect.bottom > 0; state.transform = difference < 0 ? difference : 0; } else { state.fixed = offset.value > rootRect.top; } } // ... };

这里rootRect.width被用于同步吸顶时占位元素的宽度,避免页面布局抖动;rootRect.topcontainerRect.bottom则分别驱动吸顶触发与容器内回收。窗口尺寸变化时(Sticky.tsx),组件同样依赖useRect(root)重新测量,保证吸顶宽度跟随响应式布局更新。

场景二:动态高度测量

use-height组合式函数 封装了"元素高度自动追踪"逻辑,其核心正是useRect

const setHeight = () => { height.value = useRect(element).height; };

它会在onMounted、Popup 重新打开、窗口尺寸变化等时机反复调用useRect刷新高度值,并在需要适配安全区(safe area)时通过定时器补偿 iOS 上首屏高度测量不准的问题。

更多使用位置

通过源码检索可以发现,useRect还被下列组件直接引用,覆盖滚动联动、弹层定位、懒加载检测等场景:

  • List.tsx:列表滚动加载时的占位与状态判断;
  • Calendar.tsx 与 CalendarMonth.tsx:日历滚动定位;
  • DropdownMenu.tsx 与 DropdownItem.tsx:下拉菜单的弹出层定位;
  • IndexBar.tsx 与 IndexAnchor.tsx:索引栏锚点定位;
  • FloatingBubble.tsx、Signature.tsx、SwipeCell.tsx 等。

这些组件在渲染与交互的关键路径上调用useRect,验证了它在真实业务中的高频价值。

使用注意事项与最佳实践

  1. 在挂载后再测量getBoundingClientRect要求元素存在于文档中。请务必在onMountednextTick之后调用,否则空 ref 会走全零兜底分支,得到width: 0height: 0
  2. 与滚动的关系:返回值是相对视口的坐标,页面滚动会导致top/left变化。若需要元素在文档中的绝对位置,需叠加滚动偏移量。
  3. 避免在布局抖动中高频调用getBoundingClientRect会强制浏览器进行样式计算与布局(reflow),在滚动事件中频繁调用可能影响性能。可参考 Sticky 组件"仅在scroll事件回调中测量,其余时机复用缓存值"的做法。
  4. 隐藏元素返回 0:使用display: none等隐藏元素测量结果恒为 0,Vant 的isHidden工具会配合跳过此类场景;如果你需要测量隐藏元素的尺寸,请先使其可见。
  5. 服务端渲染(SSR)注意useRect依赖window与 DOM API,仅应在客户端执行。若需在 SSR 环境下使用,应将其放入onMounted等客户端生命周期中,避免在服务端渲染阶段直接调用。

总结

useRect用不到 20 行代码,为 Vue 开发者提供了对getBoundingClientRect的优雅封装:自动解包 ref、兼容Window、空值兜底,返回值保持标准DOMRect语义。无论是独立业务开发中的元素测量,还是深入阅读 Vant 源码时理解 Sticky、List、DropdownMenu 等组件的定位逻辑,掌握它都能让你的 Vue 3 开发事半功倍。若想系统了解@vant/use提供的其他组合式 API(如useScrollParentuseCountDownuseEventListener等),可参阅 Composables 总览文档。

【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 21:24:37

UG NX与MFC对话框交互控制技术详解

1. 项目概述&#xff1a;UG界面与MFC对话框的交互控制在NX二次开发领域&#xff0c;MFC对话框与UG界面的无缝集成是一个常见但颇具挑战性的需求。当我们需要在NX环境中嵌入自定义功能界面时&#xff0c;往往面临两个核心问题&#xff1a;如何准确定位UG主窗口&#xff0c;以及如…

作者头像 李华
网站建设 2026/9/12 21:24:25

VS2017配置OpenGL完整指南:GLFW+GLAD环境搭建与常见报错排查

碰没碰过这种经历&#xff1a;照着教程把GLFW、GLAD下载好&#xff0c;包含目录也加了&#xff0c;附加依赖项也填了&#xff0c;一编译却蹦出来几十个“无法解析的外部符号”。我当时配这套环境的时候&#xff0c;Windows10刚出不久&#xff0c;VS2017也还算新&#xff0c;网上…

作者头像 李华
网站建设 2026/9/12 21:20:24

跨语言开发轻量化!MCP 工具的 npx/uvx 客户端解决80%开发环境痛点

作为搞互联网开发的, 你近来肯定屡次刷到过好些关于 “工具轻量化” 的谈论, 之前某个技术社区开展发起的那个 “2024 开发工具选择的调研” 当中, 有一项数据格外引人注目: 超出 72% 的程序员都去会吧 “给” 如果环境设置配置格外繁杂难操当成是跨语言开发的首要难题, 甚至还…

作者头像 李华
网站建设 2026/9/12 21:20:11

AI浪潮下,人人都要吃透的“第二语言”——Python

哈喽大家好&#xff0c;我是数码俱乐部&#xff0c;今天又来为大家带来经验分享&#xff01;身为数码领域深耕多年的博主, 这些年接触过数不清的数码爱好者, 也接触过众多职场打工人, 还接触过不少学生朋友, 涵盖这么多人, 大家手里的设备种类繁多, 从手机、笔记本、平板, 一直…

作者头像 李华
网站建设 2026/9/12 21:19:41

联想M73/M720迷你主机真香现场!办公学习塞进巴掌大盒子?

这小小的方块, 在我的书桌之上, 一待便是三个月, 如今呢, 它已然掌控了我的PPT领域, 占据了调试的平台, 就连周末用于剪辑vlog的副屏输出, 也被其纳入麾下——它可不是什么玩具, 而是具备隐形力量的生产力核弹。它的体积比Pro稍微薄那么一点, 其重量大约等同于两罐气泡水, 把电…

作者头像 李华