Vant 组合式 API 实战:useWindowSize 响应式监听浏览器窗口尺寸

原创2026-09-11 23:44:431,359 阅读
文章标签:前端UI组件

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 仓库内部也已投入实际使用,可作为最佳实践的参考:

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 的组件库,它都是一个值得直接复用的轻量级工具函数。

登录后查看全文
vant