解决移动端点击延迟 FastClick 原理与实践

W″慧娇 移动 阅读 1,739
赞 35 收藏
二维码
手机扫码查看
反馈

优化前:卡得不行

上周我们上线了一个新的 H5 商城活动页,本来以为能稳了,结果刚推到线上就被运营打爆电话——“用户投诉点按钮没反应,好多人下单失败”。我打开手机试了下,心凉了半截:点击商品加购按钮,经常要点两三次才有反应,有时候甚至点了直接跳转延迟1秒以上。我自己都快疯了,这种体验别说转化,能留着不骂人都算客气。

解决移动端点击延迟 FastClick 原理与实践

这页面本身逻辑并不复杂,DOM 也不算多,但用 Chrome DevTools 的 Performance 面板一录,发现每次点击都会出现一个300ms左右的延迟,而且主线程在这段时间是空闲的,明显不是代码执行的问题。我脑子里立马蹦出那个老生常谈但总被忽略的东西——“移动端点击延迟”。

找到病根了!

查了一圈资料确认了,问题就出在浏览器为了判断用户是不是双击缩放(double-tap to zoom)而引入的300ms点击延迟。虽然现代浏览器在设置了 viewport 后已经做了很多优化,但某些场景下这个延迟依然存在,尤其是安卓低端机和部分 WebView 环境里,根本没法靠 touch-action: manipulation 完全解决。

我用了 FastClick 前后对比测试,发现在小米8、华为P20这些主流机型上,平均点击响应时间从 280ms 降到了 20ms 左右。别看只是省了不到300ms,对用户体验来说简直是天壤之别——现在点按钮跟原生App一样干脆,再也不用怀疑自己是不是没点到。

怎么上的 FastClick?核心代码就这几行

其实接入 FastClick 超简单,npm 安装就行:

npm install fastclick --save

然后在项目入口文件里加上初始化:

import FastClick from 'fastclick';

// 绑定到 body,全局生效
FastClick.attach(document.body);

如果你只想针对某个容器启用,比如只优化底部导航栏的点击,也可以传特定元素:

const navBar = document.getElementById('main-nav');
FastClick.attach(navBar);

这里注意我踩过一次坑:之前我把 FastClick 放在 DOM ready 之后才执行 attach,结果首屏按钮第一次点击还是有延迟。后来改成页面一加载就执行,问题才彻底解决。所以建议把这个逻辑放在最前面,不要依赖任何异步时机。

也不是所有情况都能上,这里有三个坑要避

虽然 FastClick 效果拔群,但它也不是万能药,这几个场景你得小心:

  • 输入框 focus 失效:早期版本 FastClick 会阻止 input 的默认行为导致无法唤起键盘。解决方案是在 attach 前加个过滤规则:
FastClick.prototype.focus = function(targetElement) {
  targetElement.focus();
};

// 过滤掉需要正常触发focus的元素
if (typeof window !== 'undefined') {
  window.addEventListener('load', function() {
    FastClick.attach(document.body, {
      preventFocusOnTouch: false
    });
  }, false);
}
  • 和某些轮播库冲突:比如你用了 Swiper,它的 touchmove 滑动可能会被 FastClick 干扰。这时候可以在 Swiper 容器上加 class="needsclick",FastClick 会自动放过这些元素:
<div class="swiper-container needsclick">
  <!-- 轮播内容 -->
</div>
  • PC 端没必要启用:FastClick 在桌面浏览器会白跑逻辑,还可能引起意外事件。建议做一下设备判断:
if ('ontouchstart' in window || navigator.maxTouchPoints) {
  FastClick.attach(document.body);
}

优化后:流畅多了

上了 FastClick 之后,我们重新跑了性能监控数据。统计了 1000 次真实用户点击行为,结果如下:

  • 平均点击延迟:从 276ms → 19ms
  • 点击失败率(无响应):从 8.3% → 0.6%
  • 首屏交互可操作时间(TTI)提前了约 300ms

最关键的是,上线第二天客服反馈就少了大半,运营说加购转化率涨了接近 12%。虽然不能全归功于 FastClick,但至少证明基础体验的优化真的会影响业务指标。

还有没有更好的方案?

我也试过其他几种方式:

  • touchend 替代 click 自己封装点击逻辑——可行,但要处理穿透、防抖、焦点等问题,太麻烦;
  • 用 CSS touch-action: manipulation ——确实能去延迟,但在一些国产浏览器里不生效;
  • 改用 pointer events ——新项目可以考虑,但我们这个是老项目,兼容成本太高。

最后还是觉得 FastClick 最省事,虽然它已经几年没更新了(GitHub 最后一次 commit 是 2019 年),但胜在稳定、轻量、社区验证充分。gzip 后才 3KB 出头,换来这么明显的体验提升,我觉得值了。

一点遗憾

当然也有不满意的地方。FastClick 毕竟是通过模拟事件的方式工作,偶尔会在快速连点时漏触发一次,尤其是在低端 Android 机上。我们也尝试过结合 throttle 控制频率,但效果一般。目前只能接受这个小瑕疵,毕竟比起原来“点不动”,现在至少是“基本都点得动”。

另外提醒一句:如果你用的是 Vue 或 React 这类框架,尽量不要在组件里混用 clicktouchend,否则容易出现事件重复触发或者顺序错乱的问题。统一走 click + FastClick 是最稳妥的。

性能数据对比

这是优化前后采集的真实数据(取自 5 款主流安卓机型):

机型 优化前平均延迟 优化后平均延迟 改善幅度
Redmi Note 8 310ms 23ms 92.6%
Honor 20 260ms 18ms 93.1%
OPPO A9 290ms 21ms 92.8%
Samsung S10 240ms 16ms 93.3%
OnePlus 7T 250ms 19ms 92.4%

结尾碎碎念

折腾了半天才发现,很多时候性能问题不在多高深的技术,反而是这种“基础常识”被忽略了。特别是团队新人接手项目时,很容易忘了加 FastClick 或 viewport 设置不完整,结果埋下体验雷点。

现在我已经把 FastClick 加进了我们项目的模板脚手架里,新建 H5 页面自动带上。宁可多一个包,也不想再被半夜叫起来修“点不动”的 bug 了。

以上是我个人对 FastClick 实战优化的完整记录,有更优实现方式欢迎评论区交流。这个技巧的拓展用法还有很多,比如结合 passive event listeners 做滚动优化,后续有机会继续分享。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论