Vant 组合式 API 详解:usePageVisibility 页面可见状态监听
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
usePageVisibility是 Vant 底层依赖包@vant/use提供的一个组合式 API,用于在 Vue 3 项目中获取页面的可见状态(visible/hidden)。在移动端 Web 开发中,它常用于处理「页面切换到后台自动暂停播放、回到前台自动恢复」这类场景,例如 Vant 的 Swipe 轮播组件就是基于它实现自动播放的暂停与恢复。读完本文,你将掌握该 API 的完整用法、返回值语义、底层实现原理,以及在真实组件中的应用方式。
一、什么是 usePageVisibility
usePageVisibility是一个封装了浏览器 Page Visibility API 的组合式函数,返回一个类型为Ref<VisibilityState>的响应式引用,实时反映页面当前的可见状态:
visible:页面当前可见(用户正在浏览当前标签页)hidden:页面当前不可见(例如切换到其他标签页、最小化浏览器、或 App 切入后台)
该 API 属于 Vant 对外提供的组合式 API 家族之一,与useWindowSize、useEventListener、useCountDown等一同维护在 packages/vant-use 包中,并通过 packages/vant-use/src/index.ts 统一对外导出。Vant 组件库自身也重度依赖这些 API,因此「Vant 用户复用它们」是官方文档明确推荐的用法(见 vant-use-intro.zh-CN.md)。
二、安装与引入
虽然@vant/use已经作为 Vant 的依赖随包安装,官方仍然推荐显式安装它以获得独立的类型提示和版本管理:
# with npm npm i @vant/use # with yarn yarn add @vant/use # with pnpm pnpm add @vant/use # with Bun bun add @vant/use该包以 Vue 3 为 peer 依赖(vue: ^3.0.0,见 packages/vant-use/package.json),模块格式同时提供 ESM(dist/index.js)与 CJS(dist/index.cjs),并带有完整的 TypeScript 类型声明(dist/index.d.ts),因此可以直接获得VisibilityState等类型的自动补全。
三、基本用法
在 Vue 3 组件的setup中调用usePageVisibility()即可获取页面可见状态,配合watch监听状态变化:
import { watch } from 'vue'; import { usePageVisibility } from '@vant/use'; export default { setup() { const pageVisibility = usePageVisibility(); watch(pageVisibility, (value) => { console.log('visibility: ', value); }); }, };当用户切换标签页或最小化窗口时,控制台会打印出visibility: hidden;重新回到页面时打印visibility: visible。由于返回值本身就是一个Ref,你也可以使用<script setup>语法并直接以pageVisibility.value读取当前状态,或在模板中通过pageVisibility响应式渲染。
典型应用:自动暂停耗时任务
实际业务中,页面不可见时应当主动释放资源或暂停高频操作(如轮询请求、倒计时、动画),避免后台运行时造成不必要的性能开销与流量消耗:
import { watch, onBeforeUnmount } from 'vue'; import { usePageVisibility } from '@vant/use'; const pageVisibility = usePageVisibility(); let timer = null; watch(pageVisibility, (value) => { if (value === 'visible') { startPolling(); } else { stopPolling(); } }); onBeforeUnmount(stopPolling);四、API 详解
类型定义
type VisibilityState = 'visible' | 'hidden'; function usePageVisibility(): Ref<VisibilityState>;返回值
| 参数 | 说明 | 类型 |
|---|---|---|
| visibilityState | 页面当前的可见状态,visible为可见,hidden为隐藏 | Ref<VisibilityState> |
几个值得注意的使用细节:
- 返回值是模块级单例:
usePageVisibility内部将响应式状态缓存为模块级变量(源码见 packages/vant-use/src/usePageVisibility/index.ts),这意味着在同一个页面中多次调用该函数,返回的是同一个Ref对象,状态全局共享且不会重复绑定事件监听,避免内存泄漏与多余开销。 - 可在 setup 之外调用:由于不依赖组件实例上下文,它同样可以像
useWindowSize一样在普通模块或工具函数中调用。 - 初始值语义:首次调用时状态初始化为
visible,随后在浏览器环境下立即依据document.hidden同步一次真实状态。也就是说,如果页面一开始就处于隐藏状态(例如页面在后台被打开),第一次读取即可拿到正确的hidden,而非固定为visible。 - SSR 安全:在非浏览器环境下(
typeof window === 'undefined'),函数只返回初始的visible状态,不会尝试访问document,因此可以安全地用于服务端渲染场景。
五、底层实现原理
usePageVisibility的完整实现只有短短 20 余行(packages/vant-use/src/usePageVisibility/index.ts),却清晰地展示了三个关键设计:
import { ref, Ref } from 'vue'; import { inBrowser } from '../utils'; type VisibilityState = 'hidden' | 'visible'; let visibility: Ref<VisibilityState>; export function usePageVisibility() { if (!visibility) { visibility = ref<VisibilityState>('visible'); if (inBrowser) { const update = () => { visibility.value = document.hidden ? 'hidden' : 'visible'; }; update(); window.addEventListener('visibilitychange', update); } } return visibility; }1. 懒初始化单例模式:利用模块级变量visibility做缓存,if (!visibility)保证事件监听只注册一次。这与 changelog 中「Improve usePageVisibility event bindings performance」(packages/vant-use/changelog.md)的记录相吻合,说明该实现正是为了优化重复调用时的绑定性能。
2. 数据来源与事件驱动:状态的唯一数据源是document.hidden,状态更新由浏览器的visibilitychange事件驱动。每次事件触发时执行update函数,将document.hidden映射为'hidden' | 'visible'并写入ref,从而驱动 Vue 的响应式更新。值得说明的是,浏览器原生 Page Visibility API 还提供document.visibilityState(其取值为visible、hidden、prerender等),而 Vant 选择只关心二值状态,简化了上层使用的心智负担。
3. 环境守卫:inBrowser来自 packages/vant-use/src/utils.ts 中的typeof window !== 'undefined'判断,配合首次调用即执行update()的逻辑,既保证了 SSR 下的安全执行,又确保了首帧状态正确。
六、源码级应用案例:Swipe 自动播放的暂停与恢复
usePageVisibility并非一个「只存在于文档中的 API」,Vant 核心组件 Swipe(轮播)就把它作为自动播放的核心控制逻辑。在 packages/vant/src/swipe/Swipe.tsx 中:
watch(usePageVisibility(), (visible) => { if (visible === 'visible') { autoplay(); } else { stopAutoplay(); } });这段代码体现了非常典型的实战模式:
- 页面可见时调用
autoplay()启动轮播定时器; - 页面隐藏(切后台、锁屏)时调用
stopAutoplay()停止定时器; - 回到页面后再次自动恢复播放,无需用户手动干预。
这验证了usePageVisibility的核心价值:用几行代码将「页面生命周期」与「业务行为」解耦,让任何需要感知页面可见性的业务(视频播放、倒计时、动画、轮询)都能以统一的方式实现。
七、注意事项
- 兼容性前提:该 API 依赖浏览器的 Page Visibility API 与
visibilitychange事件,主流现代浏览器均支持;在使用前可确认目标运行环境(WebView、浏览器版本)对document.hidden的支持情况。 - 不要在 watch 回调中做重型同步操作:
visibilitychange在移动端可能伴随系统级事件频繁触发,回调中应只做轻量状态切换,重型逻辑建议异步执行。 - 单例共享语义:由于返回的是模块级共享的
Ref,不同组件监听的是同一状态源,适合「全局只需要一份可见性状态」的场景,无需自行做状态提升。
综上,usePageVisibility以极小的 API 面(一个函数、一个返回对象)解决了移动端开发中高频出现的「感知页面前后台切换」问题,配合 packages/vant/docs/markdown/use-page-visibility.en-US.md 中的英文说明与 packages/vant/docs/markdown/vant-use-intro.zh-CN.md 的 API 总览,你可以快速在业务中落地使用。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考