本文作者:姚泽源
移动端React阻止TouchMove事件冒泡
本周主要工作是基于React实现Picker组件, 类似于下面这样.
但发现一个问题: 拖拽选项列表时, 页面会跟着一起滑动.
这个问题比较常见, 一般而言, 我们会在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\"",
}