• 精选
  • 会员

图解浏览器的工作原理——浏览器对事件的处理

2018年11月20日  来源:InfoQ 作者: 提供人:t.call.p@126.c......
摘要:可能每一个前端工程师都想要理解浏览器的工作原理。我们希望知道从在浏览器地址栏中输入 url 到页面展现的短短几秒内浏览器究竟做了什么;我们希望了解平时常常听说的各种代码优化方案是究竟为什么能起到优化的作用;我们希望更细化的了解浏览器的渲染流程。

浏览器对事件的处理

浏览器通过对不同事件的处理来满足各种交互需求,这一部分我们一起看看从浏览器的视角,事件是什么,在此我们先主要考虑鼠标事件。

在浏览器的看来,用户的所有手势都是输入,鼠标滚动,悬置,点击等等都是。

当用户在屏幕上触发诸如 touch 等手势时,首先收到手势信息的是 Browser process, 不过 Browser process 只会感知到在哪里发生了手势,对 tab 内内容的处理是还是由渲染进程控制的。

事件发生时,浏览器进程会发送事件类型及相应的坐标给渲染进程,渲染进程随后找到事件对象并执行所有绑定在其上的相关事件处理函数。

史上最全!图解浏览器的工作原理

事件从浏览器进程传送给渲染进程

前文中,我们提到过合成器可以独立于主线程之外通过合成栅格化层平滑的处理滚动。如果页面中没有绑定相关事件,组合器线程可以独立于主线程创建组合帧。如果页面绑定了相关事件处理器,主线程就不得不出来工作了。这时候合成器线程会怎么处理呢?

这里涉及到一个专业名词「理解非快速滚动区域(non-fast scrollable region)」由于执行 JS 是主线程的工作,当页面合成时,合成器线程会标记页面中绑定有事件处理器的区域为 non-fast scrollable region ,如果存在这个标注,合成器线程会把发生在此处的事件发送给主线程,如果事件不是发生在这些区域,合成器线程则会直接合成新的帧而不用等到主线程的响应。

史上最全!图解浏览器的工作原理

涉及 non-fast scrollable region 的事件,合成器线程会通知主线程进行相关处理。

web 开发中常用的事件处理模式是事件委托,基于事件冒泡,我们常常在最顶层绑定事件:

document.body.addEventListener('touchstart', event => { if (event.target === area) { event.preventDefault(); }});

上述做法很常见,但是如果从浏览器的角度看,整个页面都成了 non-fast scrollable region 了。

这意味着即使操作的是页面无绑定事件处理器的区域,每次输入时,合成器线程也需要和主线程通信并等待反馈,流畅的合成器独立处理合成帧的模式就失效了。

史上最全!图解浏览器的工作原理

由于事件绑定在最顶部,整个页面都成为了 non-fast scrollable region。

为了防止这种情况,我们可以为事件处理器传递 passive: true 做为参数,这样写就能让浏览器即监听相关事件,又让组合器线程在等等主线程响应前构建新的组合帧。

document.body.addEventListener('touchstart', event => { if (event.target === area) { event.preventDefault() } }, {passive: true});

不过上述写法可能又会带来另外一个问题,假设某个区域你只想要水平滚动,使用 passive: true 可以实现平滑滚动,但是垂直方向的滚动可能会先于event.preventDefault()发生,此时可以通过 event.cancelable 来防止这种情况。

document.body.addEventListener('pointermove', event => { if (event.cancelable) { event.preventDefault(); // block the native scroll /* * do what you want the application to do here */ } }, {passive: true});

也可以使用 css 属性 touch-action 来完全消除事件处理器的影响,如:

#area { touch-action: pan-x; }

查找到事件对象

当组合器线程发送输入事件给主线程时,主线程首先会进行命中测试(hit test)来查找对应的事件目标,命中测试会基于渲染过程中生成的绘制记录( paint records )查找事件发生坐标下存在的元素。

史上最全!图解浏览器的工作原理

主线程依据绘制记录查找事件相关元素。

事件的优化

一般我们屏幕的刷新速率为 60fps,但是某些事件的触发量会不止这个值,出于优化的目的,Chrome 会合并连续的事件 (如 wheel, mousewheel, mousemove, pointermove, touchmove ),并延迟到下一帧渲染时候执行 。

而如 keydown, keyup, mouseup, mousedown, touchstart, 和 touchend 等非连续性事件则会立即被触发。

史上最全!图解浏览器的工作原理


Chrome 会合并连续事件到下一帧触发。

合并事件虽然能提示性能,但是如果你的应用是绘画等,则很难绘制一条平滑的曲线了,此时可以使用 getCoalescedEvents API 来获取组合的事件。示例代码如下:

window.addEventListener('pointermove', event => { const events = event.getCoalescedEvents(); for (let event of events) { const x = event.pageX; const y = event.pageY; // draw a line using x and y coordinates. }});

史上最全!图解浏览器的工作原理

花了好久来整理上面的内容,整理的过程收获还挺大的,也希望这篇笔记能对你有所启发,如果有任何疑问,欢迎一起来讨论。

本文经作者授权转载,原文链接为:

https://zhuanlan.zhihu.com/p/47407398

参考链接

https://developers.google.com/web/updates/2018/09/inside-browser-part1

https://developers.google.com/web/updates/2018/09/inside-browser-part2

https://developers.google.com/web/updates/2018/09/inside-browser-part3

https://developers.google.com/web/updates/2018/09/inside-browser-part4

https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/#Layered_representation

浏览器 / 工作原理 / 事件

如涉及版权,请著作权人与本网站联系,删除或支付费用事宜。

0000
相关核心文章
t.call.p@126.c...... ————最新文章