百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

现代浏览器内部机制(四):事件

bigegpt 2024-08-09 11:16 2 浏览

作者:kyrieliu

转发链接:https://mp.weixin.qq.com/s/h0rwZr7FDPpE30TxlF5lZw

目录

现代浏览器内部机制(一):图解多进程架构

现代浏览器内部机制(二):导航这件小事

现代浏览器内部机制(三):渲染进程的一生


现代浏览器内部机制(四):事件 本篇

前言

终于到最后一篇了!作为这个系列的最后一篇文章。在之前的文章中,我们了解了现在浏览器的多进程架构、导航以及渲染进程和合成器。在这篇文章中,我们将了解到合成器是如何在用户输入时流畅的处理交互的。

从浏览器的角度定义输入事件

当提到“输入事件”时,你可能会想到在文本域中打字或是鼠标的点击事件,但在浏览器看来,用户的任何动作都意味着“输入”。鼠标滚轮的滚动是一种输入事件,触摸或者鼠标滑过也是一种输入事件。

当用户的交互行为发生时(比如触摸点击屏幕),浏览器进程会第一个感知到这个用户行为,但也仅仅是感知而已,因为浏览器 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 体验。通过组织我们的代码对浏览器更加友好,也能改善用户体验,可谓一举两得一石二鸟一箭双雕!

作者:kyrieliu

转发链接:https://mp.weixin.qq.com/s/h0rwZr7FDPpE30TxlF5lZw

相关推荐

10w qps缓存数据库——Redis(redis缓存调优)

一、Redis数据库介绍:Redis:非关系型缓存数据库nosql:非关系型数据库没有表,没有表与表之间的关系,更不存在外键存储数据的形式为key:values的形式c语言写的服务(监听端口),用来存...

Redis系列专题4--Redis配置参数详解

本文基于windowsX64,3.2.100版本讲解,不同版本默认配置参数不同在Redis中,Redis的根目录中有一个配置文件(redis.conf,windows下为redis.windows....

开源一夏 | 23 张图,4500 字从入门到精通解释 Redis

redis是目前出场率最高的NoSQL数据库,同时也是一个开源的数据结构存储系统,在缓存、数据库、消息处理等场景使用的非常多,本文瑞哥就带着大家用一篇文章入门这个强大的开源数据库——Redis。...

redis的简单与集群搭建(redis建立集群)

Redis是什么?是开源免费用c语言编写的单线程高性能的(key-value形式)内存数据库,基于内存运行并支持持久化的nosql数据库作用主要用来做缓存,单不仅仅是做缓存,比如:redis的计数器生...

推荐几个好用Redis图形化客户端工具

RedisPlushttps://gitee.com/MaxBill/RedisPlusRedisPlus是为Redis可视化管理开发的一款开源免费的桌面客户端软件,支持Windows、Linux...

关于Redis在windows上运行及fork函数问题

Redis在将数据库进行持久化操作时,需要fork一个进程,但是windows并不支持fork,导致在持久化操作期间,Redis必须阻塞所有的客户端直至持久化操作完成。微软的一些工程师花费时间在解决在...

你必须懂的Redis十大应用场景(redis常见应用场景)

Redis作为一款高性能的键值存储数据库,在互联网业务中有着广泛的应用。今天,我们就来详细盘点一下Redis的十大常用业务场景,并附上Golang的示例代码和简图,帮助大家更好地理解和应用Redis。...

极简Redis配置(redis的配置)

一、概述Redis的配置文件位于Redis安装目录下,文件名为redis.conf(Windows名为redis.windows.conf,linux下的是redis.conf)你可以通过C...

什么是redis,怎么启动及如何压测

从今天起咱们一起来学习一下关于“redis监控与调优”的内容。一、Redis介绍Redis是一种高级key-value数据库。它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富。...

一款全新Redis UI可视化管理工具,支持WebUI和桌面——P3X Redis UI

介绍P3XRedisUI这是一个非常实用的RedisGUI,提供响应式WebUI访问或作为桌面应用程序使用,桌面端是跨平台的,而且完美支持中文界面。Githubhttps://github....

windows系统的服务器快速部署java项目环境地址

1、mysql:https://dev.mysql.com/downloads/mysql/(msi安装包)2、redis:https://github.com/tporadowski/redis/r...

window11 下 redis 下载与安装(windows安装redis客户端)

#热爱编程是一种怎样的体验#window11下redis下载与安装1)各个版本redis下载(windows)https://github.com/MicrosoftArchive/r...

一款轻量级的Redis客户端工具,贼好用!

使用命令行来操作Redis是一件非常麻烦的事情,我们一般会选用客户端工具来操作Redis。今天给大家分享一款好用的Redis客户端工具TinyRDM,它的界面清新又优雅,希望对大家有所帮助!简介Ti...

一个.NET开发且功能强大的Windows远程控制系统

我们致力于探索、分享和推荐最新的实用技术栈、开源项目、框架和实用工具。每天都有新鲜的开源资讯等待你的发现!项目介绍SiMayRemoteMonitorOS是一个基于Windows的远程控制系统,完...

Redis客户端工具详解(4款主流工具)

大家好,我是mikechen。Redis是大型架构的基石,也是大厂最爱考察内容,今天就给大家重点详解4款Redis工具@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集...