Vant 组合式 API 实战:useWindowSize 响应式监听浏览器窗口尺寸
Vant 组合式 API 实战:useWindowSize 响应式监听浏览器窗口尺寸
useWindowSize 是 Vant 内置组合式 API 中用于获取浏览器窗口视口(viewport)宽度与高度的工具函数,它返回响应式的 width 和 height 引用,并在窗口大小变化时自动更新。在基于 Vue 3 的移动端 / 桌面端项目中,它可以帮助你轻松实现"根据窗口尺寸调整布局、控制组件显隐、触发自适应逻辑"等常见需求。读完本文,你将掌握 useWindowSize 的安装引入、基本用法、完整 API,以及其底层源码实现原理与项目内真实使用案例。
功能简介
useWindowSize 的核心能力只有一句话:获取浏览器窗口的视口宽度和高度,并在窗口大小变化时自动更新。它由 Vant 的独立工具包 @vant/use 提供,是 Vant 官方维护的 Vue Composition API 集合中的一个成员(API 列表总览)。
该函数不需要接收任何参数,调用后立即返回一组响应式数据,适合在组件 setup 阶段直接使用,也适合在模块顶层以解构方式全局共享(详见下文"项目内实际应用")。
安装与引入
虽然 @vant/use 已经作为 Vant 的依赖被包含在内(package.json 中声明了 vue: ^3.0.0 的 peerDependencies),官方仍然推荐显式安装该包,以便直接使用其全部组合式 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
安装完成后,从 @vant/use 中按需引入即可:
import { useWindowSize } from '@vant/use';
const { width, height } = useWindowSize();
基本用法
根据 useWindowSize 官方文档,在 Vue 3 组件的 setup 中即可获取窗口尺寸,并通过 watch 监听尺寸变化:
import { watch } from 'vue';
import { useWindowSize } from '@vant/use';
export default {
setup() {
const { width, height } = useWindowSize();
console.log(width.value); // -> width of browser window
console.log(height.value); // -> height of browser window
watch([width, height], () => {
console.log('window resized');
});
},
};
需要注意:width 和 height 是 Vue 的 Ref 对象,读取时必须通过 .value 访问。在模板中则会被自动解包,可直接使用 {{ width }}。
结合 computed / watchEffect 做自适应
由于返回的是响应式引用,你可以直接将其与 computed、watchEffect 等组合,实现基于窗口尺寸的自适应逻辑,例如:
import { computed } from 'vue';
import { useWindowSize } from '@vant/use';
const { width, height } = useWindowSize();
// 是否处于移动端窄屏布局
const isMobileLayout = computed(() => width.value < 768);
// 弹层可用高度 = 视口高度 - 顶部占位
const availableHeight = computed(() => height.value - 48);
API
类型声明
function useWindowSize(): {
width: Ref<number>;
height: Ref<number>;
};
返回值
| 名称 | 说明 | 类型 |
|---|---|---|
| width | 浏览器窗口的宽度 | Ref<number> |
| height | 浏览器窗口的高度 | Ref<number> |
函数无参数、无其他副作用配置项,两个返回值均为只读的 Ref<number>,初始值在浏览器环境下会被立即赋值为当前 window.innerWidth 与 window.innerHeight。
源码级原理剖析
useWindowSize 的实现非常精简,完整源码位于 packages/vant-use/src/useWindowSize/index.ts。理解它有助于你把握该 API 的性能特征与适用边界:
import { ref, Ref } from 'vue';
import { inBrowser } from '../utils';
let width: Ref<number>;
let height: Ref<number>;
export function useWindowSize() {
if (!width) {
width = ref(0);
height = ref(0);
if (inBrowser) {
const update = () => {
width.value = window.innerWidth;
height.value = window.innerHeight;
};
update();
window.addEventListener('resize', update, { passive: true });
window.addEventListener('orientationchange', update, { passive: true });
}
}
return { width, height };
}
从源码结构可以梳理出以下关键设计:
- 模块级单例共享:
width、height被声明在模块顶层(模块作用域),并通过if (!width)保证无论调用多少次useWindowSize(),全局只会创建一组 ref、只绑定一次事件监听。这意味着在多个组件中重复调用不会产生重复监听器,也不会有状态不同步问题——这是 Vant 官方文档中"自动更新"语义的底层保障。 - SSR 安全:初始化前通过
inBrowser(定义于 packages/vant-use/src/utils.ts,即typeof window !== 'undefined')判断运行环境。在服务端渲染(SSR)场景下,window不存在,此时不会触发update(),width、height保持初始值0,不会抛出运行时错误。 - 双事件监听:同时监听
resize(窗口尺寸变化)与orientationchange(移动端屏幕方向旋转)两个事件,覆盖桌面浏览器拉伸窗口与移动设备横竖屏切换两类典型场景。 - passive 优化:事件监听使用
{ passive: true }选项,明确告知浏览器该监听器不会调用preventDefault(),从而获得滚动性能优化。 - 无需手动清理:由于监听器是模块级的全局单例,其生命周期与页面一致,不需要在组件卸载时移除,这也是该 API 不需要返回
onUnmounted清理函数的原因。
项目内实际应用案例
useWindowSize 不仅在文档中作为示例存在,在 Vant 仓库内部也已投入实际使用,可作为最佳实践的参考:
- 全局窗口尺寸共享:在 packages/vant/src/utils/dom.ts 中,Vant 直接在模块顶层解构导出:
export const { width: windowWidth, height: windowHeight } = useWindowSize();
这样整个 dom.ts 及其导入方都能以非响应式上下文读取当前窗口尺寸,且由于单例机制,不会重复注册监听器。从源码结构看,这种"模块顶层解构 + 命名导出"的写法非常适合需要跨模块共享窗口尺寸的工具库场景。
-
组件 demo 中的响应式布局:在 floating-panel/demo/index.vue 中,通过
const { height: windowHeight } = useWindowSize()获取窗口高度,用于计算浮层面板的可视区域高度,属于典型的"按视口尺寸计算组件布局"用法。 -
单元测试中的配合使用:在 floating-bubble/test/index.spec.ts 中,测试用例先调用
useWindowSize()再配合mockGetBoundingClientRect模拟尺寸,验证了该 API 在测试环境下也能正常初始化。
使用场景与注意事项
典型使用场景:
- 移动端 H5 页面根据视口高度计算弹层、底部安全区或内容区的可用高度;
- 页面需要监听窗口尺寸变化以切换布局(如横向 / 纵向、桌面 / 移动);
- 需要与
useRect(获取元素大小及其相对视口的位置)配合计算元素在窗口中的实际位置——两者均来自@vant/use,可组合使用。
注意事项:
width、height均为Ref<number>,在 JavaScript 中需通过.value访问;在模板中自动解包;- 首次调用前不会预初始化,SSR 环境下初始值为
0,客户端 hydrate 后才会被赋值为真实视口尺寸,若用于首屏渲染判断,需考虑这一时序; - 全局单例意味着监听器在整个页面生命周期内持续存在,对于极高频的 resize 场景建议在业务侧自行节流(如配合 useRaf 管理动画帧),避免在回调中执行重计算;
- 该 API 只反映
window视口尺寸,若要监听某个具体 DOM 元素的大小变化,应使用useRect或ResizeObserver。
小结
useWindowSize 以不到 30 行的源码实现了"获取窗口视口尺寸 + 自动响应变化"的完整能力,并通过模块级单例、inBrowser 环境判断、双事件监听等设计兼顾了性能、SSR 安全与多端场景。无论是开发独立业务组件,还是构建类似 Vant 的组件库,它都是一个值得直接复用的轻量级工具函数。