React Native Web 安卓h5 Touchable onPress 触发两次问题解决 绝地灬酷狼 2023-06-25 02:21 1阅读 0赞 在维护基于 React Native Web 的 h5 项目时,遇到这样的一个 bug,在部分安卓 h5 页面一些点击事件会稳定触发两边,这样的问题在 iOS 和 pc 调试 h5 都是好的,甚至同一机型只要另外换一个浏览器可能也会有不同的效果。 在调试阶段,发现只要把 `TouchableHighlight` 等组件都干掉,换成 div,点击事件也从 `onPress` 改到 `onClick`,就全部是好的,不过由于是三端项目,需要 `TouchableHighlight` 这些 React Native 组件,当然不能这么改了了事,不过也给我定位到可能是 React Native Web `Touchable` 类组件的 bug 定位到是这个问题,很快可以找到对应问题 issue 以及 RNW 作者的回复和解决 链接 #### 管中窥豹 #### 根据 issue 里大家的讨论结合后面作者 necolas 的修复改动,我们可以加一些 console 来看下引起问题的原因 首先在有问题的 onPress 函数里添加 `console.log('触发 onPress 函数')` 然后在 `node_modules/react-native-web/dist/modules/ResponderEventPlugin/index.js` 找到以下的代码(在备注的地方加入console) ResponderEventPlugin.extractEvents = function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { var hasActiveTouches = ResponderTouchHistoryStore.touchHistory.numberActiveTouches > 0; var eventType = nativeEvent.type; // 在这加 console console.log('responder >>> ', topLevelType, Date.now() - lastActiveTouchTimestamp) var shouldSkipMouseAfterTouch = false; if (eventType.indexOf('touch') > -1) { lastActiveTouchTimestamp = Date.now(); } else if (lastActiveTouchTimestamp && eventType.indexOf('mouse') > -1) { var now = Date.now(); shouldSkipMouseAfterTouch = now - lastActiveTouchTimestamp < 250; } if ( // Filter out mousemove and mouseup events when a touch hasn't started yet (eventType === 'mousemove' || eventType === 'mouseup') && !hasActiveTouches || // Filter out events from wheel/middle and right click. nativeEvent.button === 1 || nativeEvent.button === 2 || // Filter out mouse events that browsers dispatch immediately after touch events end // Prevents the REP from calling handlers twice for touch interactions. // See #802 and #932. shouldSkipMouseAfterTouch) { return; } var normalizedEvent = normalizeNativeEvent(nativeEvent); return originalExtractEvents.call(ResponderEventPlugin, topLevelType, targetInst, normalizedEvent, nativeEventTarget); }; 然后我们看一下效果 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70] 可以看到 onPress 确实触发了两次,且第一次在 `touchend` 之后,第二次在 `mouseup` 之后,所以我们可以合理推测 React Native Web 的 Touchable 组件将 onPress 应该就是在 `touchend` 和 `mouseup` 都绑定上了 那为何只有特定安卓 h5 会有这个问题呢?照上面的推论,那应该每个设备都有这个问题 我们可以换至不会触发问题的浏览器下看一下效果(在我的 onePlus 6T 下只有微信环境稳定复现,其他浏览器都不会有问题) ![在这里插入图片描述][20191224161651287.png] 似乎差不多不过就是没有触发两次 onPress,让我们再来看一下源码的实现 ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70 1] 简而言之就是,`now - lastActiveTouchstamp < 250` 时,就可以使 `shouldSkipMouseAfterTouch = true`,从而跳过绑定在 mouse 上的 onPress 事件,从而避免触发两次的情况 而我们在前面 console 后面加上的数字就是 `now - lastActiveTouchstamp` 的值,可以看到问题浏览器下,该值均在 320+,不在 250ms 的安全阈值内,无法跳过绑定在 mouse 上的 onPress 事件,因此触发两次 onPress。 #### 解决方案 #### 找到引起问题的原因,就可以需要找到一些解决方法了,下面我抛砖引玉,提供三种解决方案的思路 ##### 1. 升级 react-native-web 版本 ##### 在 0.11.7+ 的版本,RNW 作者将安全阈值更新到了 1000ms,这就可以解决绝大多数的浏览器问题。(当然了在公司某台巨卡测试机上 仍有问题,根据上面的调试,测出来的相距时间达到了 1200ms+) > [commit 信息][commit] ##### 2. 利用 postinstall 钩子函数处理 ##### 不过目前项目已经很难做依赖库升级的情况下,我们可以利用 npm 的 postinstall 钩子函数,写一些脚本在 `npm i` 后执行,人为修改本地 `node_modules` 里的文件 举个例子,可以利用 `sed` 命令来处理 sed -i '' "s/250/1500/g" ./node_modules/react-native-web/dist/modules/ResponderEventPlugin/index.js 这种方式很灵活,可以灵活改动安全阈值,比如我就把 250 改到了 1500 ##### 3. 使用 preventDefault 函数 ##### [移動端瀏覽器 :當 Touch Event 與 Mouse Event 同時存在的時候][_ Touch Event _ Mouse Event] ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70 2] 从上面的文章可以知道,我们可以在 `touchend` handler 中执行 `event.preventDefault()` 去取消 mouse event 的发送 也就是说,我们可以在有问题的 onPress 里加上 `event.preventDefault()` 就可以了(上面文章打不开的,网上还有其他很多优质博客,这里不再赘述) [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70]: https://img-blog.csdnimg.cn/20191224161640868.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70 [20191224161651287.png]: https://img-blog.csdnimg.cn/20191224161651287.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70 1]: https://img-blog.csdnimg.cn/20191224161700562.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70 [commit]: https://github.com/necolas/react-native-web/commit/c3cbd53a8a32367a16f7cdfde264ddcd40d95876 [_ Touch Event _ Mouse Event]: https://medium.com/frochu/touch-and-mouse-together-76fb69114c04 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ_size_16_color_FFFFFF_t_70 2]: https://img-blog.csdnimg.cn/20191224161708296.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly96d2tra2sxLmJsb2cuY3Nkbi5uZXQ=,size_16,color_FFFFFF,t_70
还没有评论,来说两句吧...