谷歌官方博客:深入了解现代浏览器系列(其四)
给前端以福利,给编程以复利。大家好,我是大家的林语冰。
00. 写在前面
在上一篇博客中,我们了解了渲染过程,并了解了合成器。在这篇文章中,我们将了解当用户输入时,合成器如何实现流畅的交互。
这是深入了解 Chrome 系列博客的“最后之舞”:研究它如何处理我们的代码来显示网站。
免责声明 本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Inside look at modern web browser (part 4)。
01. 从浏览器的视角解读输入事件
当你听到“输入事件”时,你可能只想到在文本框中键入或单击鼠标,但从浏览器的角度来看,输入意味着用户的任何手势。鼠标滚轮滚动是输入事件,触摸或鼠标悬停也是输入事件。
当触摸屏幕等用户手势发生时,浏览器进程是第一个接收该手势的进程。
然而,浏览器进程只知道手势发生的位置,因为选项卡内的内容是由渲染器进程处理的。
因此,浏览器进程将 touchstart
等事件类型及其坐标发送到渲染器进程。渲染器进程通过查找事件目标,并运行附加的事件侦听器,妥善处理事件。
02. 了解非快速滚动区域
由于运行 JavaScript 是主线程的工作,因此当合成页面时,合成器线程会将附加了事件处理程序的页面区域标记为“非快速滚动区域”。
通过获得此信息,如果事件发生在该区域,合成器线程可以确保将输入事件发送到主线程。
如果输入事件来自该区域之外,那么合成器线程将继续合成新帧,而无需等待主线程。
粉丝请注意,Web 开发中常见的事件处理模式是事件委托。
由于事件冒泡,你可以在最顶层元素附加一个事件处理程序,并根据事件目标委托任务。
举个栗子:
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
由于你只需为所有元素编写一个事件处理程序,因此这种事件委托模式的人体工程学设计极具吸引力。
但是,如果从浏览器的角度查看此代码,现在整个页面都被标记为非快速滚动区域。 这意味着,即使你的应用程序不关心页面某些部分的输入,合成器线程也必须与主线程通信并在每次输入事件进入时等待它。
因此,合成器的平滑滚动能力被打败了。
为了减轻这种情况的发生,你可以在事件侦听器中传递 passive: true
选项。这向浏览器暗示,你仍然希望在主线程中侦听事件,但合成器也可以继续合成新帧。
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true});
03. 检查活动是否可以取消
请想象一下,页面中有一个框,你希望将滚动方向限制为仅水平滚动。
在指针事件中使用 passive: true
选项,意味着页面滚动可以平滑,但垂直滚动可能在你想要 preventDefault
时开始,以限制滚动方向。
你可以使用 event.cancelable
方法来检查这一点。
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
// 阻塞原生滚动
event.preventDefault();
}
}, {passive: true});
或者,你可以使用 touch-action
等 CSS 规则,完全消除事件处理程序。
#area {
touch-action: pan-x;
}
04. 寻找事件目标
当合成器线程向主线程发送输入事件时,首先要运行的是命中测试来查找事件目标。
命中测试使用渲染过程中生成的绘制记录数据,来找出事件发生的点坐标下方的内容。
05. 最小化向主线程调度事件
典型的显示器每秒刷新屏幕 60 次,我们需要如何跟上节奏以实现流畅的动画。
对于输入,典型的触摸屏设备每秒传送触摸事件 60-120 次,典型的鼠标每秒传送事件 100 次。输入事件的保真度高于我们的屏幕可以刷新的保真度。
如果诸如 touchmove
之类的连续事件每秒发送到主线程 120 次,那么与屏幕刷新速度相比,它可能会触发过多的命中测试和 JavaScript 执行。
为了最大限度地减少对主线程的过多调用,Chrome 会合并 mousemove
、touchmove
等连续事件,并延迟调度直到下一个 requestAnimationFrame
之前。
keydown
、keyup
等离散事件会立即派发。
06. 使用 getCoalescedEvents
获取帧内事件
对于大多数 Web 应用而言,合并事件应该足以提供良好的用户体验。
但是,如果你正在构建诸如绘图应用之类的东东,并基于 touchmove
坐标放置路径,那可能会丢失绘制平滑的线条的中间坐标。
在这种情况下,你可以在指针事件中使用 getCoalescedEvents
方法,获取这些合并事件的信息。
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.
}
});
07. 将功能政策添加到你的网站
功能策略是一项新的 Web 平台功能,可以在你构建项目时为你保驾护航。
打开功能策略可以保证模的应用程序的某些行为,并防止你犯错误。
举个栗子,如果你想确保你的应用程序永远不会阻止解析,你可以根据同步脚本策略运行你的应用程序。启用 sync-script: 'none'
后,将阻止执行解析器阻塞 JavaScript。这可以防止任何代码阻塞解析器,并且浏览器不需要担心暂停解析器。
高潮总结
这是深入了解 Chrome 系列博客的“最后之舞”,通过 深入了解现代浏览器架构 系列的四篇博客,我们从宏观上理解了现代浏览器的多线程架构、网页请求以及页面绘制的幕后机制和基本流程。
当我们明白了浏览器的幕后工作机制之后,就可以从中找到关键节点,量化我们网站的性能,并提出性能优化的有效方案。
参考文献
粉丝互动
本期话题是:事件委托机制有什么注意事项?你可以在本文下方自由言论,文明科普。
欢迎持续关注“前端俱乐部”,给前端以福利,给编程以复利。
坚持阅读的小伙伴可以给自己点赞!谢谢大家的点赞,掰掰~
转载自:https://juejin.cn/post/7380292749181190159