深入了解现代 Web 浏览器(第 4 部分)合成器线程详解
Posted twinkle||cll
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入了解现代 Web 浏览器(第 4 部分)合成器线程详解相关的知识,希望对你有一定的参考价值。
输入进入合成器
这是关于 Chrome 内部的 4 部分博客系列的最后一部分;调查它如何处理我们的代码以显示网站。在上一篇文章中,我们查看了渲染过程并了解了合成器。在这篇文章中,我们将看看合成器如何在用户输入输入时实现流畅的交互。
从浏览器的角度输入事件
当您听到“输入事件”时,您可能只会想到在文本框中键入或鼠标单击,但从浏览器的角度来看,输入意味着来自用户的任何手势。鼠标滚轮滚动是一个输入事件,触摸或鼠标悬停也是一个输入事件。
当用户在屏幕上进行触摸等手势时,浏览器进程首先接收该手势。但是,浏览器进程只知道该手势发生的位置,因为选项卡内的内容由渲染器进程处理。因此浏览器进程将事件类型(如touchstart
)及其坐标发送到渲染器进程。渲染器进程通过查找事件目标并运行附加的事件侦听器来适当地处理事件。
图 1:通过浏览器进程路由到渲染器进程的输入事件
合成器接收输入事件
在上一篇文章中,我们研究了合成器如何通过合成光栅化图层来平滑处理滚动。如果没有输入事件监听器附加到页面,合成器线程可以创建一个完全独立于主线程的新复合框架。但是如果某些事件侦听器附加到页面上呢?合成器线程如何确定是否需要处理事件?
图 2:视口悬停在页面层上
由于运行 javascript 是主线程的工作,因此在合成页面时,合成器线程会将页面中附加有事件处理程序的区域标记为“非快速可滚动区域”。通过获得这些信息,合成器线程可以确保在该区域发生事件时将输入事件发送到主线程。如果输入事件来自该区域之外,则合成器线程继续合成新帧,而无需等待主线程。
图 3:非快速滚动区域的描述输入图
编写事件处理程序时要注意
Web 开发中常见的事件处理模式是事件委托。由于事件冒泡,您可以在最顶层元素附加一个事件处理程序,并根据事件目标委派任务。您可能已经看到或编写了如下代码。
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
由于您只需为所有元素编写一个事件处理程序,因此这种事件委托模式的人体工程学很有吸引力。但是,如果您从浏览器的角度来看这段代码,现在整个页面都被标记为非快速滚动区域。这意味着即使您的应用程序不关心来自页面某些部分的输入,合成器线程也必须与主线程通信并在每次输入事件进入时等待它。因此,合成器的平滑滚动能力被打败了。
图 4:覆盖整个页面的非快速可滚动区域的描述输入图
为了减轻这种情况的发生,您可以passive: true
在事件侦听器中传递选项。这向浏览器暗示您仍然希望在主线程中侦听事件,但合成器也可以继续合成新帧。
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
检查事件是否可以取消
想象一下,您希望将滚动方向限制为仅水平滚动的页面中有一个框。
passive: true
在指针事件中使用option 意味着页面滚动可以平滑,但垂直滚动可能在您想要的时候开始preventDefault
以限制滚动方向。您可以使用event.cancelable
方法对此进行检查。
图 5:部分页面固定为水平滚动的网页
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 规则 liketouch-action
完全消除事件处理程序。
#area {
touch-action: pan-x;
}
寻找事件目标
当合成器线程向主线程发送输入事件时,首先要运行的是命中测试以找到事件目标。命中测试使用渲染过程中生成的绘制记录数据来找出发生事件的点坐标下方的内容。
图 6:主线程查看绘制记录,询问在 xy 点上绘制了什么
最小化事件调度到主线程
在上一篇文章中,我们讨论了我们的典型显示器如何每秒刷新屏幕 60 次,以及我们需要如何跟上节奏以获得流畅的动画。对于输入,典型的触摸屏设备每秒传递 60-120 次触摸事件,典型的鼠标每秒传递 100 次事件。输入事件的保真度高于我们的屏幕可以刷新的保真度。
如果像这样的连续事件touchmove
每秒被发送到主线程 120 次,那么与屏幕刷新的速度相比,它可能会触发过多的命中测试和 JavaScript 执行。
图 7:事件淹没帧时间线导致页面卡顿
为了尽量减少对主线程的过多调用,Chrome 会合并连续事件(例如 wheel
, mousewheel
, mousemove
, pointermove
, touchmove
)并将调度延迟到下一个requestAnimationFrame
.
图 8:与之前相同的时间线,但事件被合并和延迟
任何离散事件,如keydown
,keyup
,mouseup
,mousedown
,touchstart
,和touchend
被立即出动。
使用getCoalescedEvents
得到帧内事件
对于大多数 Web 应用程序,合并事件应该足以提供良好的用户体验。但是,如果您正在构建诸如绘图应用程序和基于touchmove
坐标放置路径之类的东西 ,您可能会丢失中间坐标以绘制平滑线。在这种情况下,您可以使用getCoalescedEvents
指针事件中的方法来获取有关这些合并事件的信息。
图 9:左侧是平滑的触摸手势路径,右侧是合并的有限路径
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.
}
});
下一步
在本系列中,我们介绍了 Web 浏览器的内部工作原理。如果您从未想过为什么 DevTools 建议添加{passive: true}
您的事件处理程序,或者为什么您可能会async
在脚本标签中编写属性,我希望本系列能够阐明为什么浏览器需要这些信息来提供更快、更流畅的 Web 体验。
使用灯塔(Lighthouse)
如果你想让你的代码对浏览器友好,但又不知道从哪里开始, Lighthouse是一个工具,它可以对任何网站进行审计,并为你提供一份报告,说明哪些事情做得对,哪些地方需要改进。通读审核列表还可以让您了解浏览器关心的事情类型。
了解如何衡量绩效
不同站点的性能调整可能会有所不同,因此衡量站点的性能并确定最适合您站点的方法至关重要。Chrome DevTools 团队几乎没有关于如何衡量网站性能的教程 。
将功能策略添加到您的站点
如果您想采取额外的措施,Feature Policy是一项新的 Web 平台功能,可以在您构建项目时为您提供护栏。启用功能策略可保证您的应用程序的某些行为并防止您犯错误。例如,如果您想确保您的应用程序永远不会阻止解析,您可以在同步脚本策略上运行您的应用程序。当sync-script: 'none'
启用时,解析器-拦截JavaScript将从执行被防止。这可以防止您的任何代码阻塞解析器,并且浏览器无需担心暂停解析器。
总结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnkKbgYT-1630479316689)(https://developers.google.com/web/updates/images/inside-browser/part4/thanks.png)]
当我开始构建网站时,我几乎只关心如何编写代码以及如何提高工作效率。这些东西很重要,但我们也应该考虑浏览器如何处理我们编写的代码。现代浏览器一直并将继续投资于为用户提供更好的网络体验的方法。通过组织我们的代码对浏览器友好,反过来又可以改善您的用户体验。我希望你和我一起努力对浏览器友好!
非常感谢所有审阅本系列早期草稿的人,包括(但不限于):Alex Russell、 Paul Irish、 Meggin Kearney、 Eric Bidelman、 Mathias Bynens、 Addy Osmani、 Kinuko Yasuda、 Nasko Oskov和 Charlie Reis。
原文地址: https://developers.google.com/web/updates/2018/09/inside-browser-part4
以上是关于深入了解现代 Web 浏览器(第 4 部分)合成器线程详解的主要内容,如果未能解决你的问题,请参考以下文章