深入理解现代浏览器|青训营笔记

187 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第2天

古语云:工欲善其事,必先利其器。同样,作为一名前端开发者,如果想提升自己的开发效率,对前端的运行环境——浏览器的了解必不可少,多了解一些浏览器运行代码的原理,将更好的帮助我们达成目标。

本笔记基于[深入理解现代浏览器]阅读后所作,感兴趣的读者可以参考(w3c/20190603_cncuckoo_深入理解现代浏览器.md at master · 75team/w3c · GitHub)进行阅读。

本文以4个部分陈述:

  • 架构:以Chrome为例,介绍现代浏览器的实现架构。
  • 导航:从输入URL到获到HTML响应称为导航。
  • 渲染:浏览器解析HTML、下载外部资源、计算样式并把网页绘制到屏幕上。
  • 交互:用户输入事件的处理与优化。

1、架构

Web浏览器的架构,可以实现为一个进程包含多个线程,也可以实现为多个进程包含少数线程并通过IPC通信。而Chrome浏览器的架构则主要为第二种实现方法。

主要的进程及作用如下:

  • 浏览器进程:负责控制主框架部分,如地址栏、书签、前进/后退等,也处理网络请求、访问文件等任务。
  • 渲染器进程:负责在标签页中显示网站及处理事件。
  • 插件进程:控制网站用到的所有插件。
  • GPU进程:在独立的进程中处理GPU任务,绘制图形。

image.png 以我打开网页为例(可在右上角按钮中的更多工具中的任务管理器中查看):

image.png 其中包含了浏览器进程、渲染器进程(标签页)、GPU进程等,两个实用程序分别处理网路及存储任务。

Chrome的多进程架构的优点:

  • 进程相互独立。通常情况下一个标签页对应一个渲染进程,如果当前渲染器进程崩溃,只需关闭此标签页即可,对其他标签页不产生影响。
  • 安全和隔离。Chrome有限制进程数量的能力,如果进程达到上限,会安排一个进程处理多个标签页。同时,作为Chrome里程碑式特性,站点隔离可以让每个跨站点iframe都能运行一个独立的渲染进程,同时此进程共享同一个渲染器进程运行空间,使安全攻击绕开同源,确保上网安全。

2、导航

从请求网页到浏览器准备渲染网页的过程,叫做导航。

导航过程中,通过浏览器进程中的UI线程(负责绘制浏览器的按钮和地址栏)、网络线程(负责处理网络请求并从互联网接收数据)、存储线程(负责访问文件和存储数据)及渲染器进程几个部分进行相互协作来完成。

下图为导航的主要流程图:

image.png 这里还有几点补充:

  • 加载完毕后,如果又输入了其他URL,那么在导航到新站点之前,浏览器进程必须与当前的渲染器进程确认后再导航到新站点。如果此导航来自当前渲染器进程,首先通过渲染器确认,然后重复一遍导航流程,只不过这个导航请求时由渲染器进程提交给浏览器进程。
  • 另外,导航阶段还可能涉及Service Worker,即网页应用中的网络代理服务,开发者可以通过它控制什么缓存在本地,何时从网络获取新数据。

3、渲染

渲染器进程的核心任务是把HTML、CSS和JavaScript转换成用户可以交互的网页,主线程负责运行大多数客户端JavaScript代码,少量代码由工作线程处理,合成器线程和栅格化线程负责高效、平滑地渲染页面。

渲染主要与前端三剑客打交道,下图为渲染的主要阶段:

image.png

这里主要对合成方法进行补充:

  • 原始方法:把页面中当前视窗中的部分转换为像素,当滚动页面时,移动栅格化的画框,填补缺失的部分。

68747470733a2f2f70332e73736c2e7168696d672e636f6d2f743031333131356233343534353234393663392e676966.gif

现代合成方法:先通过主线程遍历布局树来创建分层树,确定绘制顺序,然后合成器线程将每一层栅格化并切成小片,栅格化线程再将每一小片转换为像素后保存在GPU的内存中。

68747470733a2f2f70302e73736c2e7168696d672e636f6d2f743031333739376164663564336232316435302e706e67.png

68747470733a2f2f70322e73736c2e7168696d672e636f6d2f743031313832616263386432353963626238302e706e67.png 所有小片都栅格化以后,合成器线程会收集叫做“绘制方块”(draw quad)的小片信息,以创建合成器帧。

  • 绘制方块:包含小片的内存地址、页面位置等合成页面相关的信息
  • 合成器帧:由从多绘制方块拼成的页面中的一帧 然后将合成器帧通过IPC提交给浏览器进程并发送给GPU,最终显示在屏幕上。

68747470733a2f2f70302e73736c2e7168696d672e636f6d2f743031363131663161613361313062323566332e706e67.png

4、交互

交互包括了来自用户的任何输入:鼠标滚轮转动、触摸屏幕、鼠标悬停等。

  • 触摸事件:浏览器进程接收到触摸位置,然后将位置递送给渲染器处理。

68747470733a2f2f70302e73736c2e7168696d672e636f6d2f743031333238346465366237343865326137392e706e67.png

  • 输入事件:由渲染器进程中的合成器线程处理的,如果页面上没有注册事件监听程序(即根据事件目标来运行注册的监听程序),那合成器线程可以完全独立于主线程生成新的合成器帧;如果页面上注册了事件监听程序,合成器线程会给附加了事件处理程序的页面区域打上“Non-Fast Scrollable Region”的记号。有了这个记号,合成器线程就可以在该区域发生事件时把事件发送给主线程(JS)。

68747470733a2f2f70312e73736c2e7168696d672e636f6d2f743031626234613631353365383039616639372e706e67.png 当整个页面都被打上“Non-Fast Scrollable Region”时,可以通过在注册事件时传入passive: true。此时,主线程和合成器线程会一同工作。

document.body.addEventListener('touchstart', evt => {
  ...
}, { passive: true })

最后,关于事件高触发频率的问题,高触发频率则意味着主线程过多的测试与执行,为解决此问题,Chrome会合并(coalesce)连续触发的事件(wheelmousewheelmousemovepointermovetouchmove),并将它们延迟到恰好在下一次requestAnimationFrame之前派发。

68747470733a2f2f70342e73736c2e7168696d672e636f6d2f743031623238656239316432653864633034342e706e67.png

参考文献:

[深入理解现代浏览器](w3c/20190603_cncuckoo_深入理解现代浏览器.md at master · 75team/w3c · GitHub)

总结:

本笔记主要是阅读[深入理解现代浏览器]这篇文章后所作,通过此篇文章,让我了解了浏览器时如何运行前端开发的代码,也让我对浏览器的架构等有了更深一步的认识。