合成器有输入源
本文是“Chrome 浏览器”系列博文(共 4 部分)中的最后一部分;调查它如何处理 我们的代码来显示网站在上一篇博文中,我们介绍了渲染过程并了解了合成器。在本文中,我们将 我们看看在用户输入内容时,合成器如何实现流畅的交互。
从浏览器的角度输入事件
当你听到“输入事件”时您可能只会想到在文本框中进行输入或点击鼠标, 从浏览器的角度来看,输入表示用户执行的任何手势。鼠标滚轮滚动是一项输入 事件,触摸或鼠标悬停也是一个输入事件。
当在屏幕上发生触摸等用户手势时,浏览器进程就是接收
手势。但是,浏览器进程只能知道该手势的发生位置,
标签页内的内容由渲染程序进程处理。因此,浏览器进程将事件
类型(如 touchstart
)及其坐标传递给渲染程序进程。渲染程序进程负责处理
通过查找事件目标并运行附加的事件监听器来相应地触发该事件。
![输入事件](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/input-event-265a73164e715.png?authuser=8&hl=sk)
合成器接收输入事件
在上一篇博文中,我们了解了合成器如何通过 光栅化图层。如果页面未附加任何输入事件监听器,则合成器线程 创建完全独立于主线程的新复合框架。但如果某些活动 监听器附加到了网页?合成器线程如何确定事件是否需要 处理方式?
了解不可快速滚动的区域
由于运行 JavaScript 是主线程的工作,因此在合成页面时,合成器线程 将附加了事件处理脚本的网页区域标记为“非快速滚动区域”。修改者 获得这些信息后,合成器线程可以确保将输入事件发送到主线程 如果事件发生在该地区如果输入事件来自该区域之外,那么 合成器线程会开始合成新帧,而不等待主线程。
![受限的不可快速滚动区域](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/limited-non-fast-scrollab-376be5ee2cd6b.png?authuser=8&hl=sk)
编写事件处理脚本时要注意
Web 开发中的一种常见的事件处理模式是事件委托。由于事件气泡 可以在最顶端的元素上附加一个事件处理脚本,并根据事件目标委托任务。您 可能曾查看或编写过如下代码。
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
由于您只需为所有元素编写一个事件处理脚本,因此该事件的工效学设计是 很有吸引力但是,如果您从浏览器的角度来看 视图,现在整个页面都会被标记为不可快速滚动的区域。这意味着,即使您的 应用并不在意来自页面某些部分的输入,因此合成器线程必须 与主线程进行通信,并在每次有输入事件进入时等待主线程。因此, 合成器的流畅滚动功能失效。
![整页非快速滚动区域](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/full-page-non-fast-scroll-dd3010cc3ee0f.png?authuser=8&hl=sk)
为了减少这种情况的发生,您可以在事件中传递 passive: true
选项
监听器。这将提示浏览器您仍想监听主线程中的事件,
但合成器也可以继续合成新帧
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
检查活动是否可取消
![页面滚动](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/page-scroll-2cf8d804f8587.png?authuser=8&hl=sk)
假设您在网页上有一个框,您希望将滚动方向限制为仅水平滚动。
在指针事件中使用 passive: true
选项意味着页面可以顺畅滚动,但
自动滚动可能在您所需的时间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;
}
查找事件目标
![点击测试](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/hit-test-39f1d20855064.png?authuser=8&hl=sk)
当合成器线程向主线程发送输入事件时,首先要运行的是 以便找到事件目标点击测试使用绘制记录在渲染中生成的数据 过程找出事件发生的点坐标下面的内容。
尽可能减少向主线程分派事件的次数
在上一篇博文中,我们讨论了典型的显示屏如何每秒刷新 60 次 我们需要如何跟上流畅动画的节奏。对于输入,典型的触摸屏 设备每秒传递触摸事件 60-120 次,一般鼠标每秒传递事件 100 次 。输入事件的保真度高于屏幕可以刷新的保真度。
如果 touchmove
等连续事件每秒向主线程发送 120 次,则
与网页访问速度相比,
屏幕可以刷新。
![未过滤的事件](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/unfiltered-events-5c569da8f6466.png?authuser=8&hl=sk)
为尽可能减少对主线程的过多调用,Chrome 会合并连续事件(例如
wheel
、mousewheel
、mousemove
、pointermove
、touchmove
),并延迟调度至
就在下一个requestAnimationFrame
之前。
![合并事件](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/coalesced-events-42fbb3813612d.png?authuser=8&hl=sk)
任何离散事件,例如 keydown
、keyup
、mouseup
、mousedown
、touchstart
和 touchend
所有资源
使用 getCoalescedEvents
获取帧内事件
对于大多数 Web 应用,合并事件应该足以提供良好的用户体验。
不过,如果您要构建一些内容,例如绘制应用,
touchmove
坐标,则可能会丢失中间的坐标,以绘制平滑线。在这种情况下
则可以在指针事件中使用 getCoalescedEvents
方法获取有关这些事件的信息
合并事件
![getCoalescedEvents](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/getcoalescedevents-ab59c1cb92e1b.png?authuser=8&hl=sk)
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.
}
});
后续步骤
在本系列中,我们介绍了网络浏览器的内部工作原理。如果你从未想过
开发者工具建议在事件处理脚本上添加 {passive: true}
或编写 async
的原因
属性,我希望本系列视频能帮助您
了解浏览器为何需要使用这些属性
从而提供更快速、更顺畅的网络体验。
使用 Lighthouse
如果您希望代码对浏览器有益,但又不知道从何处入手, Lighthouse 是一款可对任何网站进行审核的工具,可为您提供 报告哪些方面做得不错,哪些方面需要改进。仔细阅读审核列表 也可以让您了解浏览器关注哪些内容。
了解如何衡量性能
性能调整可能因网站而异,因此衡量广告效果至关重要 并确定哪些内容最适合您的网站。Chrome 开发者工具团队关于 如何衡量网站性能。
向您的网站添加功能政策
如果想要更进一步,功能政策是一项新功能
Web 平台功能,可作为您构建项目时的保障。正在开启
功能政策可保证应用的某些行为,并防止您出错。
例如,如果您想确保应用永远不会阻止解析,则可以在
同步脚本政策。sync-script: 'none'
已启用后,会阻止解析器的 JavaScript
无法执行。这可以防止您的任何代码阻止解析器,而
因此浏览器无需担心暂停解析器。
小结
![谢谢](https://developer.chrome.google.cn/static/blog/inside-browser-part4/image/thank-you-cae9a20f3c237.png?authuser=8&hl=sk)
刚开始构建网站时,我几乎只关心如何编写代码,以及 有助于我提高工作效率这些都很重要,但我们还应该考虑 浏览器接受我们编写的代码。现代浏览器一直并且仍在继续投资 为用户提供更好的网络体验。我们通过整理代码来为浏览器美化 从而改善用户体验希望您和我一起为浏览器友善!
非常感谢审核本系列早期草稿的所有人,包括(但不限于) Alex Russell、 Paul Ireland, Meggin Kearney, Eric Bidelman, Mathias Bynens, Addy Osmani: Kinuko Yasuda, Nasko Oskov、 以及 Charlie Reis。
您喜欢这个系列吗?如果您对以后的帖子有任何疑问或建议, 期待在下面的评论部分或 @kosamari 提供宝贵意见 。