han's bolg - 年糕記

深入理解现代浏览器

看了深入理解现代浏览器这篇文章,写的非常好,记录一下。

开始划重点:
  1. 架构篇
  • chrome最新架构:

    • 浏览器进程:控制浏览器这个应用的chrome(主框架)部分,包括地址栏、书签、前进/后退按钮等,同时也会处理浏览器不可见的高权限任务,如发送网络请求、访问文件。

    • 渲染器进程:负责在标签页中显示网站及处理事件。

    • 插件进程:控制网站用到的所有插件。

    • GPU进程:在独立的进程中处理GPU任务。之所以放到独立的进程,是因为GPU要处理来自多个应用的请求,但要在同一个界面上绘制图形。

  • 进程处理

  1. 导航篇
  • 标签页外面的一切都由浏览器进程处理,包括以下线程:

    • UI线程负责绘制浏览器的按钮和地址栏
    • 网络线程负责处理网络请求并从互联网接收数据
    • 存储线程负责访问文件和存储数据
  • 输入url后

    • 第一步:处理输入。UI线程会判断用户输入的是查询字符串还是URL
    • 第二步:开始导航。如果输入的是URL,UI线程会通知网络线程发起网络调用,获取网站内容
    • 第三步:读取响应。服务器返回的响应体到来之后,网络线程会检查接收到的前几个字节。
    1
    如果响应是HTML文件,那下一步就是把数据交给`渲染器进程`。但如果是一个zip文件或其他文件,那就意味着是一个下载请求,需要把数据传给`下载管理器`
    • 第四步:联系渲染器进程。网络线程确认浏览器可以导航到用户请求的网站,通知UI线程数据已经准备好了。UI线程会联系渲染器进程渲染网页。
    • 第五步:提交导航。数据和渲染器进程都有了,就可以通过IPC(进程间通信)从浏览器进程渲染器进程提交导航。渲染器进程也会同时接收到不间断的HTML数据流。当浏览器进程收到渲染器进程的确认消息后,导航完成,文档加载阶段开始。
    • 最后一步:初始加载完成。提交导航之后,渲染器进程将负责加载资源和渲染页面(具体细节后面介绍)。而在“完成”渲染后(在所有iframe中的onload事件触发且执行完成后),渲染器进程会通过IPC给浏览器进程发送一个消息。此时,UI线程停止标签页上的旋转图标。

如果此时用户在地址又输入了其他URL呢?

1)浏览器进程还会重复上述步骤,导航到新站点。
2)在此之前,需要确认已渲染的网站是否关注beforeunload事件。
3)如果有beforeunload事件,必须渲染器进程执行完毕,浏览器进程才能导航到新站点。
4)导航到不同的网站时,会有一个新的独立渲染器进程负责处理新导航,而老的渲染器进程要负责处理unload之类的事件。

  1. 渲染篇
  • 标签页中的一切都由渲染器进程负责处理

    • 主线程负责运行大多数客户端JavaScript代码
    • 少量代码可能会由工作线程处理(如果用到了Web Worker或Service Worker)
    • 合成器(compositor)线程栅格化(raster)线程负责高效、平滑地渲染页面
  • 加载子资源

    • Chrome会在解析同时并发运行“预加载扫描器”,当发现HTML文档中有<img><link>时,预加载扫描器会将请求提交给浏览器进程中的网络线程
  1. 交互篇
  • 用户交互比如触摸事件发生时的过程:

    • 浏览器进程首先接收到该手势。但是,浏览器进程仅仅知道手势发生在哪里,
    • 浏览器进程会把事件类型(如touchstart)及其坐标发送给渲染器进程处理这个事件,即根据事件目标来运行注册的监听程序。因为标签页中的内容是渲染器进程处理。
  • 事件处理

    • 输入事件是由渲染器进程中的合成器线程处理的。
    • 如果页面上没有注册事件监听程序,那合成器线程可以完全独立于主线程生成新的合成器帧。
    • 如果页面上注册了事件监听程序呢?此时合成器线程怎么知道是否有事件要处理?
  • 为什么事件监听要加上passive:true?

1
2
3
document.body.addEventListener('touchstart', evt => {
...
}, { passive: true })
  • 自己看文章吧
  • 大概原理是,如果把事件监听都挂载到document.body, 那么在处理每一次事件的时候,都需要渲染器进程合成器线程先进行js事件的判断,再合成新帧。而加了这个之后,就可以同步进行了。