[第39周]全栈之路-移动端React阻止TouchMove事件冒泡与使用npm一次启动两个命令

1,259 阅读2分钟

本文作者:姚泽源

移动端React阻止TouchMove事件冒泡

本周主要工作是基于React实现Picker组件, 类似于下面这样.

image.png

但发现一个问题: 拖拽选项列表时, 页面会跟着一起滑动.

这个问题比较常见, 一般而言, 我们会在React中使用onTouchMove接口, 捕获TouchMoveEvent事件, 然后调一下preventDefault()就可以了. 但, 没有效果!

于是查了下原因, 发现这篇文章里讲的很清楚: Chrome在54之后为了优化移动端性能, 默认在document上以passive:true方式绑定了滚动监器, 导致React中事件处理接口的preventDefault方法失效. 最佳解决方案当然是React在框架层面给与支持, 但看了一下, 相关issue从16年提出来, 到现在还没被修复...

Chrome也提供了一个解决方案: 在css中添加touch-action: none;屏蔽浏览器默认行为, 但查阅can i use后发现, iOS上不支持这个属性, 非常尴尬.

文章最后通过document.getElementById('nonius')获取元素, 单独监听touchmove事件来解决问题. 但组件库里直接用dom API显然不合适. 一筹莫展之际, 我想到了一个解决方案

看看其他组件库是怎么实现的

所以翻到了京东的一套组件, 他们的解决思路和文章中差不多, 不过是使用ref进行的事件监听

Object.keys(this.scrollHandlers).forEach(key => {
  if (key.indexOf('touch') === 0 || key.indexOf('mouse') === 0) {
    const pd = key.indexOf('move') >= 0 ? willPreventDefault : willNotPreventDefault;
    // @ts-ignore
    rootRef.addEventListener(key, this.scrollHandlers[key], pd);
  }
});
// ...
render() {
  return (
  <div className={cls} style={style} ref={this.createRootRef}>
  </div>
  );
}

✅问题解决

非webpack项目, 使用npm一次启动两个命令

在开发非webpack项目时, 经常需要启动多个命令(npm run start/npm run watch/npm run server), 调试时很麻烦.

不过antd-mobile中提供了一个解决方案: 使用concurrently

使用效果

{
    "start": "cross-env NODE_ENV=development concurrently \"cross-env DEMO_ENV=react bisheng start -c ./site/bisheng.desktop.config.js\" \"cross-env DEMO_ENV=react bisheng start -c ./site/bisheng.kitchen.config.js\"",
}