浏览器基本工作原理(四)

172 阅读2分钟

浏览器如何处理页面操作

接收输入事件

  • 浏览器进程接收键盘、鼠标事件、触摸手势等输入事件。浏览器进程仅知道手势发生位置,选项卡内部内容由渲染进程处理。
  • 浏览器进程将事件类型、坐标发送给渲染进程
  • 渲染进程通过查找事件目标并运行附加的事件侦听器来适当地处理事件
  • 合成器线程接入输入事件

Input event routed through the browser process to the renderer process.webp

非快速可滚动区域

  • 合成页面时,合成器线程标记页面的一个区域,该区域将事件处理程序附加为“非快速可滚动区域”。
  • 通过获取此信息,合成器线程可以确保在该区域中发生事件时将输入事件发送到运行 JavaScript 的主线程。如果输入事件来自该区域之外,则合成器线程在不等待主线程的情况下继续合成新帧。

Diagram of described input to the non-fast scrollable region.webp

事件处理

下面程序中,整个页面都被标记为非快速可滚动区域,合成器线程也必须与主线程通信,并在每次输入事件进入时等待它,最终影响合成器平滑滚动能力。

Diagram of described input to the non-fast scrollable region covering an entire page.png

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
});

检查取消事件

通过 event.cancelable和event.preventDefault()检查取消事件。

也可通过CSS来完全消除事件处理程序。

Diagram of described input to the non-fast scrollable region covering an entire page.png

document.body.addEventListener(
    'pointermove',
    event = > {
    if (event.cancelable) {
        event.preventDefault();
    }
}, {
    passive: true
});
#area {
    touch - action: pan - x;
}

查找 event.target

当合成器线程向主线程发送输入事件时,首先要运行的是命中测试以查找事件目标。命中测试使用在渲染过程中生成的绘制记录数据来找出事件发生的点坐标下面的内容。

The main thread looking at the paint records asking what's drawn on x.y point.webp

最小化事件派发到主线程

  • 屏幕刷新率:60 次/秒
  • 触摸屏触摸事件:60-120次/秒
  • 鼠标:100次/秒

输入事件具有比屏幕刷新更高的保真度。主线程中触发过快的连续事件,会触发过多的命中测试和JS执行,导致页面抖动。为减少对主线程过度调用,Chrome合并连续事件(如 wheel,mousewheel,mousemove,pointermove, touchmove)并延迟调度,直到下一个requestAnimationFrame执行。

离散事件则立即执行,如keydown,keyup,mouseup,mousedown,touchstart,touchend

Events flooding the frame timeline causing page jank.webp

Same timeline as before but event being coalesced and delayed.webp

使用 getCoalescedEvents 得到帧内事件

大多数Web应用程序,合并事件应足以提供良好的用户体验。构建绘制应用程序并根据touchmove坐标放置路径等可能会丢失中间坐标以绘制平滑线,可以使用getCoalescedEvents指针事件中的方法来获取这些合并事件的信息。

Smooth touch gesture path on the left, coalesced limited path on the right.webp

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.
    }
});