iOS端Chrome屏幕旋转后window.innerHeight取值错误问题咨询
iOS端Chrome v94横竖屏切换后window.innerHeight取值错误修复方案
问题根因
iOS端Chrome v94的底层WebKit内核存在时序缺陷:横竖屏切换时,resize事件会先于视口尺寸更新完成触发,此时读取window.innerHeight/innerWidth会拿到上一个状态的宽/高和当前状态的高/宽拼接的异常值,和你测试得到的414x358、800x566这类错误值的表现完全一致。页面越复杂渲染耗时越长,bug触发概率越高,且该缺陷仅存在于iOS Chrome v94版本,其余平台/浏览器不受影响。
修复方案
- 延后视口尺寸读取时机:resize事件触发后不要立即读取尺寸,通过双重
requestAnimationFrame等待页面渲染流程完成后再取值,可规避时序问题 - 优先使用更准确的视口API:用
window.visualViewport提供的width/height属性替代传统的window.innerWidth/innerHeight,移动端场景下该API返回的视口尺寸准确度更高,不受该内核bug影响 - 新增事件兜底监听:同时监听
orientationchange事件触发尺寸更新,避免极端场景下resize事件漏触发的问题
你提供的复现代码存在一个小优化点:原useEffect未传入空依赖数组,会导致每次组件渲染时都重复绑定、解绑事件,修复代码中也同步优化了该问题。
修复后代码示例
import * as React from 'react' function App() { const [message, setMessage] = React.useState(getString()) React.useEffect(() => { const handleUpdate = () => { // 双重requestAnimationFrame等待视口更新完成 requestAnimationFrame(() => { requestAnimationFrame(() => { setMessage(getString()) }) }) } window.addEventListener('resize', handleUpdate) window.addEventListener('orientationchange', handleUpdate) return () => { window.removeEventListener('resize', handleUpdate) window.removeEventListener('orientationchange', handleUpdate) } }, []) return ( <div className="App"> <header className="App-header"> <p> {message} </p> </header> </div> ); } function getString() { // 优先用visualViewport,降级方案用原innerWidth/innerHeight if (window.visualViewport) { return 'The viewport size is ' + window.visualViewport.width + 'x' + window.visualViewport.height + '.' } return 'The viewport size is ' + window.innerWidth + 'x' + window.innerHeight + '.' } export default App;
内容的提问来源于stack exchange,提问作者Roman Kurbatov
相关产品推荐
相关产品推荐

