🎨
Notes
  • 持续更新中...
  • articles
    • browser
      • 深入理解现代浏览器 - 导航
      • 深入理解现代浏览器 - 架构
      • 深入理解现代浏览器 - 交互
      • 深入理解现代浏览器 - 渲染器进程
    • dsa
      • DSA - 并查集
      • DSA - 哈希表
      • DSA - AVL 树
      • DSA - 二叉树
      • 快速选择
      • Big O 算法复杂度
      • DSA - 栈和队列
      • DSA - 前缀树 Trie
      • DSA - 图
      • DSA - 链表
      • DSA - 递归
    • typescript
      • TypeScript 学习笔记 - 任意属性 (Indexable Types)
      • 力扣的 TypeScript 面试题
      • TypeScript 学习笔记 - as const
      • TypeScript 学习笔记 - infer
    • network
      • Internet Protocol (IP)
      • 计算机网络基础
      • 如何分辨同源和同站
      • DNS 如何查询 IP 地址?
    • vue
      • Nuxt.js 入门
      • 从零实现一个 Mini Vue
      • 从零实现一个简单的 VDOM 引擎
      • 从零实现一个响应式状态管理
    • sorting
      • 排序 - 归并排序
      • 排序 - 冒泡排序
      • 排序 - 选择排序
      • 排序 - 计数排序
      • 排序 - 插入排序
    • compile
      • Compiler and Interpreter
      • Just-In-Time (JIT) Compilers
      • 编译流程
    • others
      • 什么是上下文无关语法
      • 如何在终端打印出有颜色的字
    • dev-ops
      • github-actions
        • GitHub Action 简介
        • GitHub Actions for CI
    • workflow
      • 用 Node 写一个 cli
      • 如何规范 git commit 信息
      • 如何监听 git hooks
      • 如何规范代码风格 - prettier
      • 如何发布一个 npm package
      • 如何规范代码质量 - eslint
    • design-pattern
      • 代理模式
      • 单例模式
      • 策略模式
    • security
      • 点击劫持
      • CSP 内容安全策略
    • javascript
      • 尾调用优化
      • 4种常见的内存泄漏及解决方法
    • unit-test
      • Test Vuejs Application - Chapter 2
      • Test Vuejs Application - Chapter 1
      • Vue Unit Test Intro
    • performance
      • HTTP 缓存
      • 如何优化图片资源
Powered by GitBook
On this page
  • 从浏览器的角度看用户输入
  • 合成器线程接收事件
  • 非快速滑动区域(non-fast scrollable region)
  • 定位事件对象
  • 降低向主线程发送事件的频率
  • getCoalescedEvents()
  • 小结

Was this helpful?

  1. articles
  2. browser

深入理解现代浏览器 - 交互

从浏览器的角度看用户输入

在浏览器看来,用户输入包括:

  • 在输入框中输入文字

  • 点击鼠标

  • 滚动鼠标滚轮

  • 移动鼠标,或者手指在屏幕上滑动

  • ...

当这些事件发生时,浏览器进程是最先收到消息的,但是,浏览器进程只知道发生的事件类型以及事件发生的坐标,因为网页内容是渲染器进程在管,所以浏览器进程会把事件类型(event type)和事件发生的坐标传送给渲染器进程,渲染器进程再找到事件目标(event target)并触发事件处理函数(event handler)。

合成器线程接收事件

如果用户触发了页面滑动事件,浏览器进程会把事件信息发送到渲染器进程中的合成器线程,合成器线程的处理分两种情况:

  • 如果页面上没有绑定事件处理函数,那么合成器线程只需要把已经栅格化的图层合成一个新的合成器帧就可以了,这样整个滑动过程会非常丝滑。

  • 如果页面绑定了事件处理函数,合成器线程会通知主线程来执行相应的事件处理函数。

非快速滑动区域(non-fast scrollable region)

当合成器线程在合成页面时,它会把页面中有绑定事件处理函数的区域标记为“非快速滑动区域”,如果用户输入事件发生在这些区域,合成器线程会通知主线程去处理这些事件,否则合成器线程就可以不用和主线程通信,直接合成新的合成器帧即可。

那么问题来了,我们在绑定事件处理函数的时候,常常会使用事件代理模式,试想一下我们把整个页面的事件处理都交给 <body> 元素来代理监听,那整个页面都会被合成器线程标记为“非快速滑动区域”,那合成器线程的丝滑滑动优点就发挥不了作用了,现在每次页面滑动时合成器线程都得先和主线程通信并等待主线程的回应。

为了合成器丝滑滑动和事件代理这两个优点能共存,我们可以在监听事件的时候传递第三个参数 { passive: true },意思是,主线程还是照样监听事件,但合成器线程就不用等待主线程的回复了,可以在通知主线程之后直接开始合成新的合成器帧。

定位事件对象

当主线程从合成器线程那里接收到事件信息之后,它要做的第一件事情就是命中测试(hit test),找到事件对象,命中测试会使用在渲染过程中生成的绘制记录(paint record)来找到在事件发生坐标的具体是哪个元素。

降低向主线程发送事件的频率

一般触屏设备每秒会触发 60-120 次 touch 事件,一般鼠标每秒会触发 100 鼠标事件,而显示器每秒只会刷新 60 次。假如每秒连续向主线程发送 120 次 touchmove,就会触发 120 次命中测试,由于屏幕刷新次数远小于 120 次,有些命中测试就有点多余了。

所以 Chrome 会把连续触发的事件(如 wheel, mousewheel, mousemove, pointermove, touchmove)结合起来,等到下一个 requestAnimationFrame() 调用前再把事件信息发送给主线程。

不过非连续的事件,如 keydown, keyup, mouseup, mousedown, touchstart, touchend 都是立刻就通知主线程的。

getCoalescedEvents()

对于一般应用程序,Chrome 合并连续触发事件的这个操作并不会影响用户体验,不过如果有需要,可以通过 event.getCoalescedEvents() 来获取被合并的事件的信息。

小结

这一节介绍了浏览器是怎么处理用户输入的。

Previous深入理解现代浏览器 - 架构Next深入理解现代浏览器 - 渲染器进程

Last updated 4 years ago

Was this helpful?