移动端兼容问题概览

移动端兼容问题高度依赖机型、系统版本、内置 WebView 环境。同一套页面在 PC 正常,往往仅在特定手机或 App 内复现。

根本诱因:

  • 新旧设备规则割裂:老旧机型内核停止更新,前缀、视口、fixed 定位沿用旧标准;新款机型引入刘海屏、安全区、动态工具栏,带来全新视口适配难题。
  • WebView 环境不统一:系统 Safari、Chrome、第三方浏览器、微信等 App 内置浏览器,对滚动、触摸、软键盘、证书校验的实现逻辑各不相同。
  • 网络环境更复杂:弱网、4G、Wi‑Fi 切换、代理环境普遍,请求超时、资源加载中断、TLS 握手失败概率远高于桌面端。
  • 低端设备性能瓶颈:合成层滥用、大图、未节流的 scrolltouchmove 监听,桌面无感的性能损耗,在手机上直接表现为卡顿。
  • Web 标准落地进度不一致dvhvisualViewportinputmode 等新 CSS、Web API,各内核支持程度差异明显,不能直接以新版 Chrome 行为作为通用标准。

iOS 跟随滚动元素延迟、慢一拍

iOS 视觉滚动运行在 UI 线程,页面立刻滚动;而 scroll 事件在滚动位置更新后异步派发。页面已经滑动完成,监听 scroll 同步位置的元素才跟随移动,视觉上存在延迟。Android 滚动与 scroll 事件基本同步,不存在该现象。

优化方案

  • 滚动过程(手指滑动阶段):依靠 touchmove 获取触摸坐标同步元素位置;
  • 惯性滚动(松手后):无 touchmove 事件,只能依赖 scroll,搭配 requestAnimationFrame 实时读取 scrollTop

注意:挂载在根节点的 touchmove 默认 passive: true;如果需要执行 preventDefault() 阻止滚动,必须显式传入 { passive: false },否则会产生冲突。

input disabled、readonly 在 iOS 常见坑

  • disabled:iOS 下输入框文字会变得极淡甚至看不见,Android 文字正常。仅展示、禁止编辑场景优先使用 readonly
  • readonly:iOS 点击依旧唤起软键盘。不需要焦点时添加 pointer-events: none;允许点击但禁止弹出键盘,使用 inputmode="none",或在 focus 事件内执行 blur()
<input readonly inputmode="none" value="仅展示内容" />

移动端 click 是否还存在 300ms 延迟?

页面设置 width=device-width 时,现代 Chrome、Safari、Firefox 已经移除双击缩放带来的 300ms 延迟,新项目无需默认引入 FastClick。该延迟诞生初衷是区分单击与双击缩放;开启设备宽度视口后,浏览器不再等待判定时间。

若依旧感知点击发黏,给可交互元素添加样式,关闭双击缩放:

a,
button,
[role='button'] {
  touch-action: manipulation;
}

触摸事件执行顺序保持不变:touchstarttouchmovetouchendclick

注意:FastClick 在部分机型会造成 <input> 点击失效;Zepto tap 事件存在点击穿透问题,二者不建议作为通用方案。

iOS 软键盘弹出,fixed 元素错位

iOS 唤起软键盘时,只会修改可视视口,position: fixed 依旧基于布局视口计算,底部悬浮按钮、弹窗会上浮、遮挡或消失。多数 Android 不存在该问题;少数 WebView 收起键盘后页面无法自动复位。

无万能 CSS 方案,推荐适配策略:

  • 输入区域避开 fixed 布局,底部栏放入滚动容器,或改用 absolute
  • 使用 Visual Viewport API 监听视口变化,基于可视视口高度动态定位;
  • 输入框失焦后执行 window.scrollTo(0, 0),修复 Android 键盘收起后的页面错位。
window.visualViewport.addEventListener('resize', () => {
  document.documentElement.style.setProperty('--vvh',
    `${window.visualViewport.height}px`,
  )
})

备注:微信等 App WebView 软键盘行为由客户端控制,仅调整 H5 代码无法彻底解决所有场景。

Android 软键盘收起, fixed 弹窗错位

常见于微信内置 WebView:fixed 弹窗内部聚焦输入框,键盘顶起页面,点击空白区域收起键盘后,弹窗、遮罩无法恢复原始位置。

修复方案:输入框失焦强制复位页面滚动位置

input.addEventListener('blur', () => {
  window.scrollTo(0, 0)
})

iOS html-to-image 导出图片空白

使用 html-to-image(同类库 dom-to-image)在 iOS 导出画布时,首次调用经常出现画布空白、内容残缺,二次调用恢复正常。根源是 Safari DOM 渲染写入 Canvas 的时序兼容问题。

封装建议:等待页面字体、图片资源全部加载完成后执行导出;iOS 环境增加二次调用兜底策略。

消除 transition 过渡闪屏

WebKit 内核中 3D 变换、CSS 过渡偶尔出现白屏闪烁、背面内容透出。针对闪烁节点添加三维属性,隐藏背面:

.layer {
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}

注意:仅作用于出现闪屏的元素,不要全局添加。大量元素开启 3D 属性会生成过多合成层,造成动画卡顿。动画依旧优先使用 transformopacity,避免 lefttop

拨打电话、短信、邮件唤起方式

使用系统协议链接,必须由用户主动点击 <a> 触发;直接通过脚本 location.href 跳转,大量 WebView 会进行拦截。

<a href="tel:0755-10086">打电话</a>
<a href="sms:10086">发短信</a>
<a href="mailto:peun@foxmail.com">写邮件</a>

短信携带正文存在系统差异:iOS 使用 &body=,Android 使用 ?body=,需要区分系统拼接链接;sms: 协议在 iOS、Android 主流环境可用。

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-08-18 22:07:38

results matching ""

    No results matching ""