import { useEffect, useState } from 'react'; /** * 订阅 body 的宽高,在 window resize 时更新。 * @returns 当前 body 的 { width, height }(clientWidth/clientHeight) */ export const useScreenSize = () => { const [screenSize, setScreenSize] = useState<{ width: number; height: number }>({ width: 0, height: 0, }); useEffect(() => { const handleResize = () => { const body = document.getElementsByTagName('body')[0]; setScreenSize({ width: body?.clientWidth ?? 0, height: body?.clientHeight ?? 0 }); }; handleResize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return screenSize; }; /** * 订阅 ref 对应 DOM 元素的尺寸,使用 ResizeObserver 在尺寸变化时更新。 * @param ref 目标元素的 RefObject * @returns 当前元素的 { width, height }(contentRect) */ export const useRefSize = ( ref: React.RefObject ) => { const [size, setSize] = useState<{ width: number; height: number }>({ width: 0, height: 0 }); useEffect(() => { const element = ref.current; if (!element) return; setSize({ width: element.clientWidth, height: element.clientHeight, }); const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { const { width, height } = entry.contentRect; setSize({ width, height }); } }); resizeObserver.observe(element); return () => { resizeObserver.disconnect(); }; }, [ref]); return size; }; const MOBILE_BREAKPOINT = 768; const PHONE_BREAKPOINT = 640; /** * 响应式检测:基于 useScreenSize 的 body 宽度判断设备类型。 * - isMobile: 宽度 ≤ 768px(手机 + 平板) * - isPhone: 宽度 < 640px(仅手机,对应 Tailwind sm 断点以下) */ export function useResponsive() { const { width } = useScreenSize(); return { isMobile: width <= MOBILE_BREAKPOINT, isPhone: width > 0 && width < PHONE_BREAKPOINT, }; }