React中页面宽高变化时实时检测设备类型(含横屏、平板)的实现问题
设备类型检测实现方案
原方案问题根因
- useEffect依赖项不符合React响应式规则:
window.innerWidth/window.innerHeight等浏览器原生属性不属于React管理的响应式状态,值发生变更时React无法感知,因此检测逻辑只会在组件挂载时执行一次,不会随视口变化更新 - 检测逻辑维度不足:仅通过是否支持触屏区分两类设备,没有加入UA规则、尺寸规则区分平板,也没有加入横竖屏判断逻辑,无法覆盖需求指定的四类场景
具体实现方案
1. 优化视口变化监听逻辑
通过resize事件监听视口变化,搭配防抖逻辑减少高频触发带来的性能损耗,无需将window属性加入useEffect依赖。
2. 扩展检测逻辑覆盖四类场景
检测逻辑按分层规则判断:
- 非触屏设备直接归为桌面端
- 触屏设备通过UA匹配+视口尺寸判断:UA包含平板标识、或视口最小边≥768px的设备归为平板
- 剩余触屏设备判定为移动端,再通过视口宽高判断横竖屏:宽度大于高度则为移动端横屏,否则为移动端
完整代码示例
import { useState, useEffect } from 'react'; // 防抖函数,可根据需求调整等待时长 const debounce = (func, wait = 200) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; const DeviceDetectDemo = () => { const [deviceType, setDeviceType] = useState(''); const deviceDetector = () => { let hasTouchScreen = false; if ("maxTouchPoints" in navigator) { hasTouchScreen = navigator.maxTouchPoints > 0; } else if ("msMaxTouchPoints" in navigator) { hasTouchScreen = navigator.msMaxTouchPoints > 0; } else { const mQ = matchMedia("(pointer:coarse)"); if (mQ && mQ.media === "(pointer:coarse)") { hasTouchScreen = !!mQ.matches; } else if ("orientation" in window) { hasTouchScreen = true; } else { const UA = navigator.userAgent; hasTouchScreen = /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA); } } // 非触屏设备直接判定为桌面端 if (!hasTouchScreen) { setDeviceType("Desktop"); return; } const UA = navigator.userAgent; const minViewportSide = Math.min(window.innerWidth, window.innerHeight); // 判定是否为平板 const isTablet = /\b(iPad|Android|Tablet|PlayBook|Silk)\b/i.test(UA) && !/\b(Mobile|phone)\b/i.test(UA) || minViewportSide >= 768; if (isTablet) { setDeviceType("Tablet"); return; } // 移动端判断横竖屏 const isLandscape = window.innerWidth > window.innerHeight; setDeviceType(isLandscape ? "MobileLandscape" : "Mobile"); }; useEffect(() => { // 初始化执行一次 deviceDetector(); // 绑定防抖后的监听函数 const debouncedDetect = debounce(deviceDetector); window.addEventListener('resize', debouncedDetect); // 组件卸载时清理事件 return () => { window.removeEventListener('resize', debouncedDetect); }; }, []); return <div>当前设备类型:{deviceType}</div>; }; export default DeviceDetectDemo;
可选优化点
- 可根据业务实际调整平板的尺寸阈值,部分业务会将900px作为平板与移动端的分界
- 可根据业务面向的设备范围补充更多UA匹配规则,提升小众设备的识别准确率
- 横竖屏判断可补充
window.orientationAPI做老旧设备的兼容兜底 - 不想手动实现基础逻辑的场景,可使用主流React工具库内置的媒体查询Hook简化监听代码,无需手动绑定resize事件
内容的提问来源于stack exchange,提问作者Chrissisbeast
相关产品推荐
相关产品推荐

