内容简介:点赞在看,支持作者:heart:
原文: Inside Look at Modern Web Browser(part 4) [1]
作者: Mariko Kosaka [2]
译者:kyrieliu
终于到最后一篇了!作为这个系列的最后一篇文章。在之前的文章中,我们了解了现在浏览器的多进程架构、 导航 以及渲染进程和合成器。在这篇文章中,我们将了解到合成器是如何在用户输入时流畅的处理交互的。
从浏览器的角度定义输入事件
当提到“输入事件”时,你可能会想到在文本域中打字或是鼠标的点击事件,但在浏览器看来,用户的任何动作都意味着“输入”。鼠标滚轮的滚动是一种输入事件,触摸或者鼠标滑过也是一种输入事件。
当用户的交互行为发生时(比如触摸点击屏幕),浏览器进程会第一个感知到这个用户行为,但也仅仅是感知而已,因为浏览器 tab 下的内容都是由渲染进程全盘掌控着。于是浏览器进程在第一时间将用户事件的类型和坐标发送给渲染进程。渲染进程通过查找并调用对应的事件处理函数来处理这个用户输入事件。
合成器接收到输入事件
在上一篇文章中,我们研究了合成器如何通过光栅化图层来平滑的处理滚动。如果页面上没有事件监听器,合成器线程会创建一个完全独立于主线程的新的合成帧。如果页面上挂在了一些事件监听器又会发生什么呢?合成器线程又是怎样找出需要被触发的事件呢?
非快速滚动区域
因为运行 JavaScript 是主线程的任务,当一个页面被合成,合成器线程将页面上挂在了事件处理器的区域标记为“非快速滚动区域”。有了这个标记之后,合成器就能保证在对应的区域触发输入事件时可以向主线程传递这一事件。如果输入事件来自于这个区域之外,合成器则会持续合成新的帧,并不会等待主线程。
写事件处理器时要注意
在 Web 开发中一个比较常见的事件处理模型就是事件委托(代理)。因为事件的冒泡机制,开发者可以在最顶层的元素挂载一个事件处理函数,并且基于 event target 分发不同的处理逻辑。下面的代码,你可能已经司空见惯了:
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
只用写一个事件处理器就可以搞定所有的输入事件,这在工程学上是一件极具魅力的事情。当你从浏览器的视角审视这段代码的时候,你会发现整个页面都被标记成了“非快速滚动区域”。这就意味着即使你的 web app 不关心来自页面上某个位置的输入事件,但合成器线程仍然会基于这次触发的事件和主线程进行“交流”。在这种模式之下,合成器本身“平滑处理页面滚动”的能力就不复存在了。
为了减轻这种情况的发生,开发者可以给自己的事件处理器传递 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();
}
}, { passive: true });
同时,你也可以用如下 css 来帮忙消除事件处理器:
#area {
touch-action: pan-x;
}
查找 event target
当合成器线程向主线程发送了一个输入事件后,第一件事情就是通过 hit test(点击测试) 找到对应的 event target(事件目标,还是不翻译这个词比较正宗)。Hit test 利用渲染进程产生的绘制记录来找出在触发本次输入事件的坐标底下的真实元素。
减少主线程的事件处理负担
在上一篇文章中,我们讨论了主流的显示器通过每秒 60 次的频率刷新以及我们需要跟上这个节奏以实现流畅的动画效果。对于输入事件来说,主流的触摸屏会以每秒 60 到 120 次的频率向主线程传递触摸事件,大多数的鼠标事件都被以每秒 100 次的频率传递给主线程。 输入事件的保真度是普遍高于主流屏幕的刷新能力的。
如果一个持续不断的事件(比如 touchmove)在一秒内被传递给了主线程 120 次,这就会触发大量的 hit test 和 JavaScript 的执行,这么一对比,每秒 60 次的屏幕刷新速率就显得太慢了。
为了减少主线程的负担,Chrome 将常见的连续事件进行了合并(比如 wheel、mousewheel、mousemove、pointermove、touchmove 等),并且在 requestAnimationFrame
中延缓了事件的触发时机。
其他“分散触发”的事件(keydown、keyup、mouseup、touchstart、touchend 等)仍保持立即触发的策略。
通过 getCoalescedEvents 获取帧内事件
对于大多数的 web app 来说,合成事件是为了更好的用户体验。假如你在开发一款绘画的应用程序,如果你根据 touchmove 的坐标来放置路径,大概率是会丢失掉中间的坐标的,你也就无法画一条平滑的线了。这种情况下,你就可以用 getCoalescedEvents
这个方法来获取更多关于合成事件的信息。
window.addEventListener('pointermove', event => {
const evnets = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// 用这些坐标画线,稳
}
});
接下来...
在这个系列中,我们详细的探讨了现代浏览器的内部工作机制。如果你之前从来没有想过 为什么官方推荐在你的事件处理函数中添加 passive 参数 ,或者不知道为什么 在 script 标签上添加 async 属性 ,我希望这个系列能为你阐明为什么浏览器需要这些东西来提供更快、更流畅的用户体验。
Lighthouse 用起来
如果你想让自己的代码变得更加“浏览器友好”却不知道从哪里开始,不妨试试 Lighthouse [3] 吧。Lighthouse 是一个可以对网站进行审核检查的工具,会为开发者提供一份包含网站得分以及优化方案的详尽报告。
学习如何度量性能
不同的网站对于性能的需求可能不同,因此找到合适的度量方法以及优化方案是至关重要的。Chrome 的开发者 工具 团队有话说: 通过 Chrome Devtools 优化网站性能 [4] 。
给网站添加 Feature Policy
如果你想更进一步,Feature Policy 了解一下?Feature Policy 是一个新的 web 特性,它可以在开发者构建 web app 时提供“保护”。启用 feature policy 可以确保你的 web app 具备某些行为,并在一定程度上避免开发者犯错。举个例子,如果你希望保证你的 app 不会阻塞解析,你可以在同步脚本策略之下运行你的 app。当 sync-script:none
打开时,会阻塞解析的 JavaScript 都会被阻止执行。这一策略会防止任何“脚本阻塞解析”的发生,浏览器就再也不用担心解析被阻塞这件事情了。
总结
当我在构建网站时,我通常只关注怎么写代码以及怎样才能让自己的效率变得更高。这些事确实很重要,但我们也需要关注浏览器究竟会怎样处理我们的代码。现代浏览器在持续地为用户提供更好的 Web 体验。通过组织我们的代码对浏览器更加友好,也能改善用户体验,可谓一举两得一石二鸟一箭双雕!
参考资料
Inside Look at Modern Web Browser(part 4): https://developers.google.com/web/updates/2018/09/inside-browser-part4
Mariko Kosaka: https://developers.google.com/web/resources/contributors/kosamari
Lighthouse: https://developers.google.com/web/tools/lighthouse
通过 Chrome Devtools 优化网站性能: https://developers.google.com/web/tools/chrome-devtools/speed/get-started
点赞在看,支持作者:heart:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
精通数据科学:从线性回归到深度学习
唐亘 / 人民邮电出版社 / 2018-5-8 / 99.00元
数据科学是一门内涵很广的学科,它涉及到统计分析、机器学习以及计算机科学三方面的知识和技能。本书深入浅出、全面系统地介绍了这门学科的内容。 本书分为13章,最初的3章主要介绍数据科学想要解决的问题、常用的IT工具Python以及这门学科所涉及的数学基础。第4-7章主要讨论数据模型,主要包含三方面的内容:一是统计中最经典的线性回归和逻辑回归模型;二是计算机估算模型参数的随机梯度下降法,这是模型工......一起来看看 《精通数据科学:从线性回归到深度学习》 这本书的介绍吧!