likes
comments
collection
share

谷歌官方博客:深入了解现代浏览器系列(其四)

作者站长头像
站长
· 阅读数 25

给前端以福利,给编程以复利。大家好,我是大家的林语冰。

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 之前。

谷歌官方博客:深入了解现代浏览器系列(其四)

keydownkeyup 等离散事件会立即派发。

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
评论
请登录