在日常开发场景中,监听窗口变化是一个比较常见又很重要的业务功能,其实实现起来也很简单,今天就来记录一下具体的实现以及注意事项。
实现思路
在 React 中,可以通过监听 window 的 resize 事件来检测可视区域(viewport)的宽度变化。
其中实现的关键为:
- window.innerWidth:获取浏览器可视区域的宽度
- window 的 resize 事件:监听窗口大小的变化,但要注意防抖,避免触发性能问题。虽然 useEffect 只执行两次,但它注册的 resize 事件回调会在每次窗口变化时触发,而这个触发频率可能极高。
- 在组件销毁时正确清理监听器,移除 resize 事件监听,避免内存泄漏。
具体实现
import React, { useEffect, useState } from 'react';
export default function ViewportWidthTracker() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
// 监听窗口大小变化
let timeoutId = null;
const handleResize = () => {
// 每次触发 resize 时清除之前的定时器
clearTimeout(timeoutId);
// 设置新的定时器,延迟 150ms 执行
timeoutId = setTimeout(() => {
setSize({ width: window.innerWidth, height: window.innerHeight });
}, 150);
};
window.addEventListener('resize', handleResize);
// 清理监听器
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 空依赖数组确保只在组件挂载和卸载时运行
return (
<div>
<h1>当前可视区域宽度: {width}px</h1>
</div>
);
}
注意事项
- SSR 环境问题:在服务端渲染(如 Next.js)中,window 对象不存在。可通过条件判断避免错误
useEffect(() => {
if (typeof window === 'undefined') return;
// 监听逻辑
}, []);
- 组件卸载时清理:必须在 componentWillUnmount 或 useEffect 返回的清理函数中移除事件监听,防止内存泄漏。
封装自定义 hook
这个场景比较常见,可以封装为自己的 hook,方便后续调用。
import { useState, useEffect } from 'react';
function useWindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
// 监听窗口大小变化
let timeoutId = null;
const updateSize = () => {
// 每次触发 resize 时清除之前的定时器
clearTimeout(timeoutId);
// 设置新的定时器,延迟 150ms 执行
timeoutId = setTimeout(() => {
setSize({ width: window.innerWidth, height: window.innerHeight });
}, 150);
};
window.addEventListener('resize', updateSize);
updateSize();
return () => window.removeEventListener('resize', updateSize);
}, []);
return size;
}
// 使用自定义 Hook
function MyComponent() {
const size = useWindowSize();
return (
<div>
当前窗口: {size.width} x {size.height}
</div>
);
}