news 2026/9/10 6:25:37

airi × VueUse useIntersectionObserver 实战:元素可见性监听的 Composable、指令与类型声明详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
airi × VueUse useIntersectionObserver 实战:元素可见性监听的 Composable、指令与类型声明详解

airi × VueUse useIntersectionObserver 实战:元素可见性监听的 Composable、指令与类型声明详解

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

在 airi 这类以 Vue 3 + TypeScript 构建、大量依赖 VueUse 可复用组合式函数的项目(工作区通过 pnpm-workspace.yaml 的 catalog 统一锁定@vueuse/core^14.4.0)中,"元素何时进入视口"是懒加载、滚动动画、曝光统计等场景的基础能力。本文围绕仓库内的技能参考文档 useIntersectionObserver.md 展开,完整讲解useIntersectionObserver的 Composable 用法、v-intersection-observer指令用法、全部配置参数与类型声明,并结合 airi 仓库中真实使用原生IntersectionObserver的场景说明其落地价值。

核心定位:它是做什么的

useIntersectionObserver属于 VueUse 的Elements分类(见 SKILL.md 中 Elements 表格),核心职责一句话概括:检测目标元素可见性的变化(Detects changes to a target element's visibility)

它是对浏览器原生IntersectionObserverAPI 的响应式封装:把"元素是否/多可见"这一异步的、事件驱动的检测结果,变成可以驱动 Vue 响应式状态(ref)的数据源,并自动处理生命周期清理。

Composable 用法:监听模板 ref 并驱动响应式状态

文档给出的标准用法如下(注意 Vue 3.5+ 中useTemplateRef替代了getCurrentInstance().refs取法):

<script setup lang="ts"> import { useIntersectionObserver } from '@vueuse/core' import { shallowRef, useTemplateRef } from 'vue' const target = useTemplateRef('target') const targetIsVisible = shallowRef(false) const { stop } = useIntersectionObserver( target, ([entry], observerElement) => { targetIsVisible.value = entry?.isIntersecting || false }, ) </script> <template> <div ref="target"> <h1>Hello world</h1> </div> </template>

要点解析:

  • 第一个参数target接收useTemplateRef('target')的结果。从类型声明看,它支持三种形态:MaybeComputedElementRef(单个响应式元素引用)、MaybeRefOrGetter<MaybeElement[]>(元素数组的 ref/getter)、或两者的数组——也就是说它可以一次监听多个元素,甚至监听集合本身动态变化的元素。
  • 第二个参数callback就是原生IntersectionObserverCallback,签名是(entries, observer) => void。回调里解构取entries[0],用entry.isIntersecting(布尔值,是否相交)判定可见性;entry上还带有intersectionRatio(相交比例)、boundingClientRectrootBoundstime等字段,可用于更精细的逻辑。
  • 返回值解构出stop,用于手动停止观察;composable 在组件卸载时会自动清理,一般无需手动调用stop,除非你希望在卸载前就释放观察器。

targetIsVisibleshallowRef而非ref是刻意选择:它只存一个布尔值,shallowRef能避免不必要的深层代理开销——这在频繁触发(快速滚动)时是更稳妥的写法。

Directive 用法:v-intersection-observer 指令

如果不想在<script setup>里写完整的 composable,VueUse 提供了@vueuse/components中的vIntersectionObserver指令,直接在模板里绑定回调:

<script setup lang="ts"> import { vIntersectionObserver } from '@vueuse/components' import { shallowRef, useTemplateRef } from 'vue' const root = useTemplateRef('root') const isVisible = shallowRef(false) function onIntersectionObserver([entry]: IntersectionObserverEntry[]) { isVisible.value = entry?.isIntersecting || false } </script> <template> <div> <p> Scroll me down! </p> <div v-intersection-observer="onIntersectionObserver"> <p>Hello world!</p> </div> </div> <!-- with options --> <div ref="root"> <p> Scroll me down! </p> <div v-intersection-observer="[onIntersectionObserver, { root }]"> <p>Hello world!</p> </div> </div> </template>

指令绑定值有两种写法:

  1. 仅回调v-intersection-observer="onIntersectionObserver",默认以 viewport 为根、threshold: 0
  2. 回调 + 选项对象v-intersection-observer="[onIntersectionObserver, { root }]",第二个元素即UseIntersectionObserverOptions,上例把root指向另一个容器,表示"以该容器的边界(而非浏览器视口)为参照系"来判断相交——这是做容器内滚动曝光检测的关键参数。

配置参数与类型声明(完整继承)

文档给出了完整的类型声明,这里逐字段展开其含义与取值:

export interface UseIntersectionObserverOptions extends ConfigurableWindow { /** * Start the IntersectionObserver immediately on creation * @default true */ immediate?: boolean /** * The Element or Document whose bounds are used as the bounding box when testing for intersection. */ root?: MaybeComputedElementRef | Document /** * A string which specifies a set of offsets to add to the root's bounding_box when calculating intersections. */ rootMargin?: MaybeRefOrGetter<string> /** * Either a single number or an array of numbers between 0.0 and 1. * @default 0 */ threshold?: number | number[] } export interface UseIntersectionObserverReturn extends Supportable, Pausable { stop: () => void } export declare function useIntersectionObserver( target: | MaybeComputedElementRef | MaybeRefOrGetter<MaybeElement[]> | MaybeComputedElementRef[], callback: IntersectionObserverCallback, options?: UseIntersectionObserverOptions, ): UseIntersectionObserverReturn

参数速查表:

参数类型默认值说明
immediatebooleantrue创建时是否立即启动观察。设为false可实现"按需启动/暂停"(返回值含Pausable,可与isActive联动)
rootMaybeComputedElementRef \| Documentviewport作为判定参照的滚动容器。传一个容器元素后,相交检测以该容器边界为准,适用于容器内滚动场景
rootMarginMaybeRefOrGetter<string>'0px'参照边界外的偏移,语法与 CSS 的margin相同,如'100px 0px';常用来做"提前 100px 触发"的懒加载或预热
thresholdnumber \| number[]00.0–1.0 之间的单个数或数组,表示触发回调所需的最小相交比例。数组如[0.25, 0.5, 0.75]会在每个比例跨越时都触发回调
windowWindow(继承自ConfigurableWindow当前 window指定观察器所在的窗口对象,SSR 环境下可指向defaultWindow以避免访问不存在的 DOM

返回对象继承Supportable(含isSupported,SSR 下为false)、Pausable(含isActive),并额外暴露stop()手动停止。

几个值得注意的设计:

  • rootrootMargin均为响应式类型MaybeComputedElementRef/MaybeRefOrGetter),意味着可以把一个 ref 传进去,当容器元素在异步渲染后才挂载、或偏移量随布局动态变化时,composable 内部会unref并跟随变化,无需重建 observer。
  • immediate: false+ 返回值Pausable:可以把监听挂起,配合其它状态(如元素尚未创建、页面被锁定)在合适时机再激活。
  • isSupported:在 SSR(Nuxt SSR 渲染阶段)该值为false,composable 会安全地不执行浏览器 API,因此这段代码在 SSR 项目中可以无条件使用。

在 airi 仓库中的印证:原生 IntersectionObserver 的典型场景

当前仓库源码中没有直接以useIntersectionObserver命名的调用(技能文档.agents/skills/vueuse-functions是作为 Agent 开发指南随仓维护的参考集),但"用 IntersectionObserver 做可见性驱动"这一模式在 airi 的文档站组件里有真实落地:ThemedVideo.vue 中手动new IntersectionObserver(handleVisibility, {...})(第 31–58 行附近),在视频进入视口时恢复播放、离开时暂停——这正是useIntersectionObserver抽象所覆盖的场景。

对比一下两条路径:

  • 原生写法(ThemedVideo.vue 的做法):需要自己保存observer引用、在onBeforeUnmount中手动disconnect()、处理组件复用与元素替换;
  • useIntersectionObserver写法:生命周期清理、响应式 target、SSR 守卫都由 composable 承担,回调里只需要写"可见时做什么"。

从源码结构看,airi 的 stage-web、stage-ui 等包均已依赖@vueuse/core/@vueuse/shared(见 apps/stage-web/package.json、packages/stage-ui/package.json),因此任何需要"元素进出视口"驱动行为的组件,都可以按本文模式直接使用,而无需新增依赖。

典型实战映射

需求推荐配置
图片/组件懒加载(提前于视口加载)rootMargin: '200px 0px',回调中当entry.isIntersecting为 true 时加载资源,并可考虑加载后stop()
曝光/统计(进入视口才计数)threshold: 0.5,保证元素至少 50% 可见才记一次
滚动进度/分段动画threshold: [0.1, 0.25, 0.5, 0.75, 0.9],在回调中读取entry.intersectionRatio驱动样式
容器内滚动检测(如侧边栏、虚拟列表视口)root指向容器 ref,配合rootMargin收窄/扩大触发区
暂停/恢复监听immediate: false+ 返回值isActive控制,或调用stop()

小结

useIntersectionObserver的价值在于把原生IntersectionObserver的三件麻烦事——异步回调、生命周期清理、SSR 兼容——收敛为一个接收响应式 target 的 composable。掌握threshold/root/rootMargin/immediate四个参数,再配合 Composable 与v-intersection-observer指令两种接入方式,就能在 airi 这类 VueUse 深度集成的项目中覆盖绝大多数"元素可见性"需求。进一步的 API 语义可对照文档末尾指向的浏览器标准IntersectionObserver接口,以及 SKILL.md 中同属 Elements 分类的useElementVisibility(仅追踪视口内可见性)、useResizeObserver(尺寸变化)等邻近能力做选型。

【免费下载链接】airi💖🧸 Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-sama's altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi

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

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

ZYNQ-7000硬件设计复用:AD/OrCAD/Allegro可执行资料包

简介&#xff1a;本资源是一套面向FPGA工程师、嵌入式开发者及ZYNQ初学者的完整硬件设计支持包&#xff0c;聚焦Xilinx Zynq-7000系列&#xff08;AX7010/AX7020&#xff09;开发板的原理图、PCB与器件级工程资料&#xff0c;解决硬件选型、电路设计、封装复用与芯片底层理解等…

作者头像 李华
网站建设 2026/9/10 6:20:02

从Python到Rust:AI Agent框架SkillLite的性能优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华