eventPhase对滑动锁关键,因其能精准区分事件捕获、目标、冒泡三阶段:在CAPTURING_PHASE锁定下拉意图,在AT_TARGET阶段阻断默认滚动并触发刷新,在BUBBLING_PHASE交由原生处理,确保JS与原生手势协同不冲突。

在 Hybrid 混合应用中,下拉刷新的滑动锁(即防止误触发、避免与页面内其他滚动冲突)不能只靠简单监听 touchmove 位移,而需结合事件传播机制中的 eventPhase 精准判断当前触摸事件所处阶段,从而动态启用或释放滑动锁。
为什么 eventPhase 对滑动锁关键
Hybrid 页面常嵌套在原生容器(如 WebView 或自定义容器)中,原生层可能已注册了全局手势监听。当 JS 层也监听 touch 事件时,两者可能同时响应——若不区分事件阶段,JS 可能过早拦截事件(比如在捕获阶段就 preventDefault),导致原生侧无法识别滑动手势;或过晚响应(仅在冒泡阶段处理),又错过首帧方向判定。而 eventPhase 值为 1(CAPTURING_PHASE)、2(AT_TARGET)、3(BUBBLING_PHASE)可明确告知你:此刻事件正向下传递、刚到达目标、还是正向上冒泡。
在 touchstart 阶段锁定初始意图
仅在 eventPhase === 1(捕获阶段)且 scrollTop === 0 时记录起点,可确保不干扰原生滚动逻辑:
- 给容器绑定
{ capture: true }的touchstart监听器 - 在回调中检查
event.eventPhase === 1和document.documentElement.scrollTop === 0 - 满足条件才保存
touches[0].clientY并标记isPullIntent = true - 否则跳过,让事件继续向下或交由原生处理
用 eventPhase 区分手势归属权
当页面含横向 Swiper、吸顶导航等组件时,需避免垂直下拉干扰水平滑动。可在 touchmove 中按阶段做分流:
-
捕获阶段(eventPhase === 1):只做轻量判定——计算 deltaY,若连续两帧 > 5px 且方向一致,临时设
lockDirection = 'vertical' -
目标阶段(eventPhase === 2):若
lockDirection === 'vertical',调用event.preventDefault()阻止默认滚动,同时开启下拉状态渲染 - 冒泡阶段(eventPhase === 3):不再干预,交由父容器或原生层处理其他逻辑(如越界回弹动画)
在 touchend 后依阶段决定是否提交刷新
松手瞬间的判定必须严格限定在目标元素自身,避免被子组件事件干扰:
- 只在
eventPhase === 2且event.target === refreshContainer时计算最终位移 - 若位移 ≥ 60px,触发刷新请求,并设
isRefreshing = true - 若
eventPhase === 3,说明事件已冒泡到 body 或 document,忽略该次松手,防止误触发 - 刷新完成后重置所有锁状态,包括
isPullIntent和lockDirection
不复杂但容易忽略:eventPhase 不是装饰性字段,它是事件流的“时间戳”,用对了,就能让 JS 和原生手势各司其职,既保原生流畅感,又稳控 Web 层交互逻辑。

















