在日常開發場景中,監聽窗口變化是一個比較常見又很重要的業務功能,其實實現起來也很簡單,今天就來記錄一下具體的實現以及注意事項。
實現思路
在 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>);
}