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