bug描述
同事集成了uckysheet后,进入luckysheet的页面再返回其他的页面,其他页面就无法滚动了
原因
luckysheet在document上用addEventListener加了一个touchmove事件(事件回调就只做了阻止默认事件,不阻止默认事件的话在滑动的时候整个页面会乱移动),但是在destroy中只有用jq的off清空事件回调(没有removeEventListener),由于jq的off只能清空用on加的回调,所以是其他页面的滚动也被阻止默认行为了,最终结果就是其他页面无法滚动了。
解决办法
我们要做的就是在destroy的时候removeEventListener。
(luckysheet这里用addEventListener,因为jq的on无法修改passive)
1、mobile.js
//禁止微信下拉拖出微信背景
// 源码:
/*
document.addEventListener("touchmove", function(event){
event.preventDefault();
}, {
passive: false
})
*/
// END
// nby 移动端,解决影响其他页面的滚动
document.addEventListener("touchmove",Store.preDefault, {
passive: false
})
2、然后就是destroy的时候removeEventListener
// nby 移动端 解决影响其他页面的滚动
document.removeEventListener('touchmove',Store.preDefault)
3、store/index.js 中会存放一个全局的preDefault方法,就是前面使用的Store.preDefault
=======================================
疑问
1、为什么需要手动将passive设为false,true不行吗?
true为什么不行?
mdn上关于passive的介绍有如下一段:
MDN:一个布尔值,设置为
true
时,表示listener
永远不会调用preventDefault()
。如果 listener 仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
意思就是passive为true的时候,事件回调里面写了e.preventDefault(),控制台就会爆警告。
代码如下:
document.querySelector('#con').addEventListener('touchmove', function(e) {
console.log(999);
e.preventDefault()
}, {
passive: true
})
报错如下:
在这个bug中,touchmove的回调是有使用preventDefault,为了解决报错,所以需要使用passive:false。
为什么要手动设置?
passive不应该默认false吗?后来查阅mdn发现并不全是。
MDN: 根据规范,
addEventListener()
的passive
默认值始终为false
。然而,这引入了触摸事件和滚轮事件的事件监听器在浏览器尝试滚动页面时阻塞浏览器主线程的可能性——这可能会大大降低浏览器处理页面滚动时的性能。为了避免这一问题,大部分浏览器(Safari 和 Internet Explorer 除外)将文档级节点 Window、Document 和 Document.body 上的 wheel、mousewheel、touchstart 和 touchmove 事件的
passive
默认值更改为true
。如此,事件监听器便不能取消事件,也不会在用户滚动页面时阻止页面呈现。
从上面的原文可以知道,passive在大部分标签上是默认为false,但是在window、document、body等上的触摸和滚轮事件时passive的默认值是true。
所以luckysheet在document上的touchmove需要将passive手动设为false,目的是提到滚动时的性能。下面具体介绍下passive。
2、passive有啥用?
mdn关于passive还介绍了:
MDN:将
passive
设为true
可以启用性能优化,并可大幅改善应用性能
所以将 passive
设为 true
时可以改善性能,提高页面的滑动流畅度(ps也正是这一改善,出现了一些问题,微信小程序和ipad的滑动会有问题)
关于passive更多介绍见:
让页面滑动流畅得飞起的新特性:Passive Event Listeners_dj0379的博客-CSDN博客
链接文章总结:
在很久以前是单线程渲染框架,浏览器非常卡(因为内核线程包揽几乎所有工作),示意图如下
【图来自链接文章】
后来因为硬件更新,所以换成了多线程渲染框架(内核线程负责:dom树构建、元素布局、js执行、图层绘制记录等;合成线程:图层绘制实现、图层图像合成等),并且合成线程在努力展示当前帧的内容时,内核线程更在拼命处理下一帧的内容。
【图来自链接文章】
接下来就是讲chrome对于滑动、捏合这些手势输入事件(手势输入事件是由用户连续的普通输入事件组合产生,如连续的mousewheel/touchmove事件可能会生成GestureScrollBegin/GestureScrollUpdate等手势事件。)响应是非常快的,因为可以先不让内核线程去处理事件回调,而是先展示快照,让用户先滑动,但是快的前提是不能在事件回调中使用e.preventDefault(),因为只要阻止默认行为,就无法生成手势输入事件(直观就是滑不动)。
所以这也引出了passive的作用,当passive为true时,就是告诉浏览器,你(浏览器)先别看我的事件回调,不用管我在事件回调中有没有阻止默认行为,你(浏览器)尽管去滑(响应)吧。如果我既写了passive:true,也有阻止默认行为,那passive的权限更大,最终“阻止默认行为”无效(并且爆警告),页面还是能正常滚动,该咋样就咋样。
3、这个bug有什么启发?
在单页面应用中,给document、body等重要的dom加了事件后,退出页面记得要确定清空回调了,避免影响其他页面。