必须为 touchstart、touchmove、scroll 三类可取消事件显式添加 { passive: true },否则浏览器会阻塞滚动等待 JS 执行导致卡顿;Vue 中可用 .passive 修饰符,但不可与 .prevent 同时使用。
直接在 addeventlistener 中传入 { passive: true },就能让浏览器跳过等待 js 执行的环节,立即响应滚动或触摸动作。这是移动端滚动优化中最有效、最常被忽略的一环。
哪些事件必须配 { passive: true }
以下三类事件默认可取消(cancelable = true),浏览器会保守地阻塞滚动等待 JS 执行完毕,不加 passive: true 就容易卡顿:
- touchstart:哪怕只监听不做任何处理,也会触发“等待判定”机制
- touchmove:高频触发,未设 passive 时极易掉帧
-
scroll:特别是
overflow: auto或scroll的容器内滚动
正确写法与常见错误
必须用对象参数形式显式声明,不能省略大括号,也不能和旧式布尔参数混淆:
- ✅ 正确:
el.addEventListener('touchmove', handler, { passive: true }); - ❌ 错误:
el.addEventListener('touchmove', handler, true);(第三个参数是useCapture,不是passive) - ❌ 错误:
el.addEventListener('scroll', handler, { passive: true, capture: true });(capture在 touchmove/scroll 场景中极少需要,易引发逻辑混乱)
什么情况下不能用 { passive: true }
如果业务逻辑确实要阻止默认行为(比如下拉刷新、手势缩放、拖拽排序),就不能设 passive: true,否则调用 e.preventDefault() 会报错并失效:
- 仅在真正需要拦截的区域绑定非 passive 监听器(例如只在顶部下拉区监听
touchmove) - 用
e.cancelable动态判断是否允许阻止:if (e.cancelable) e.preventDefault(); - 避免在
document上全局监听touchmove并无条件preventDefault,这会直接禁用整个页面滚动
配合 Vue 使用 .passive 修饰符
Vue 模板中写法更简洁,效果等同于原生 { passive: true }:
- ✅ 正确:
- ✅ 正确:
- ❌ 错误:
(.passive和.prevent冲突)
注意:即使用了 .passive,仍建议对 onScroll 做轻量节流(如每 16ms 最多执行一次),避免高频回调挤占主线程。










