这是我参与「第四届青训营 」笔记创作活动的第2天
古语云:工欲善其事,必先利其器。同样,作为一名前端开发者,如果想提升自己的开发效率,对前端的运行环境——浏览器的了解必不可少,多了解一些浏览器运行代码的原理,将更好的帮助我们达成目标。
本笔记基于[深入理解现代浏览器]阅读后所作,感兴趣的读者可以参考(w3c/20190603_cncuckoo_深入理解现代浏览器.md at master · 75team/w3c · GitHub)进行阅读。
本文以4个部分陈述:
- 架构:以Chrome为例,介绍现代浏览器的实现架构。
- 导航:从输入URL到获到HTML响应称为导航。
- 渲染:浏览器解析HTML、下载外部资源、计算样式并把网页绘制到屏幕上。
- 交互:用户输入事件的处理与优化。
1、架构
Web浏览器的架构,可以实现为一个进程包含多个线程,也可以实现为多个进程包含少数线程并通过IPC通信。而Chrome浏览器的架构则主要为第二种实现方法。
主要的进程及作用如下:
- 浏览器进程:负责控制主框架部分,如地址栏、书签、前进/后退等,也处理网络请求、访问文件等任务。
- 渲染器进程:负责在标签页中显示网站及处理事件。
- 插件进程:控制网站用到的所有插件。
- GPU进程:在独立的进程中处理GPU任务,绘制图形。
以我打开网页为例(可在右上角按钮中的更多工具中的任务管理器中查看):
其中包含了浏览器进程、渲染器进程(标签页)、GPU进程等,两个实用程序分别处理网路及存储任务。
Chrome的多进程架构的优点:
- 进程相互独立。通常情况下一个标签页对应一个渲染进程,如果当前渲染器进程崩溃,只需关闭此标签页即可,对其他标签页不产生影响。
- 安全和隔离。Chrome有限制进程数量的能力,如果进程达到上限,会安排一个进程处理多个标签页。同时,作为Chrome里程碑式特性,站点隔离可以让每个跨站点iframe都能运行一个独立的渲染进程,同时此进程共享同一个渲染器进程运行空间,使安全攻击绕开同源,确保上网安全。
2、导航
从请求网页到浏览器准备渲染网页的过程,叫做导航。
导航过程中,通过浏览器进程中的UI线程(负责绘制浏览器的按钮和地址栏)、网络线程(负责处理网络请求并从互联网接收数据)、存储线程(负责访问文件和存储数据)及渲染器进程几个部分进行相互协作来完成。
下图为导航的主要流程图:
这里还有几点补充:
- 加载完毕后,如果又输入了其他URL,那么在导航到新站点之前,浏览器进程必须与当前的渲染器进程确认后再导航到新站点。如果此导航来自当前渲染器进程,首先通过渲染器确认,然后重复一遍导航流程,只不过这个导航请求时由渲染器进程提交给浏览器进程。
- 另外,导航阶段还可能涉及Service Worker,即网页应用中的网络代理服务,开发者可以通过它控制什么缓存在本地,何时从网络获取新数据。
3、渲染
渲染器进程的核心任务是把HTML、CSS和JavaScript转换成用户可以交互的网页,主线程负责运行大多数客户端JavaScript代码,少量代码由工作线程处理,合成器线程和栅格化线程负责高效、平滑地渲染页面。
渲染主要与前端三剑客打交道,下图为渲染的主要阶段:
这里主要对合成方法进行补充:
- 原始方法:把页面中当前视窗中的部分转换为像素,当滚动页面时,移动栅格化的画框,填补缺失的部分。
现代合成方法:先通过主线程遍历布局树来创建分层树,确定绘制顺序,然后合成器线程将每一层栅格化并切成小片,栅格化线程再将每一小片转换为像素后保存在GPU的内存中。
所有小片都栅格化以后,合成器线程会收集叫做“绘制方块”(draw quad)的小片信息,以创建合成器帧。
- 绘制方块:包含小片的内存地址、页面位置等合成页面相关的信息
- 合成器帧:由从多绘制方块拼成的页面中的一帧 然后将合成器帧通过IPC提交给浏览器进程并发送给GPU,最终显示在屏幕上。
4、交互
交互包括了来自用户的任何输入:鼠标滚轮转动、触摸屏幕、鼠标悬停等。
- 触摸事件:浏览器进程接收到触摸位置,然后将位置递送给渲染器处理。
- 输入事件:由渲染器进程中的合成器线程处理的,如果页面上没有注册事件监听程序(即根据事件目标来运行注册的监听程序),那合成器线程可以完全独立于主线程生成新的合成器帧;如果页面上注册了事件监听程序,合成器线程会给附加了事件处理程序的页面区域打上“Non-Fast Scrollable Region”的记号。有了这个记号,合成器线程就可以在该区域发生事件时把事件发送给主线程(JS)。
当整个页面都被打上“Non-Fast Scrollable Region”时,可以通过在注册事件时传入
passive: true。此时,主线程和合成器线程会一同工作。
document.body.addEventListener('touchstart', evt => {
...
}, { passive: true })
最后,关于事件高触发频率的问题,高触发频率则意味着主线程过多的测试与执行,为解决此问题,Chrome会合并(coalesce)连续触发的事件(wheel、mousewheel、mousemove、pointermove、touchmove),并将它们延迟到恰好在下一次requestAnimationFrame之前派发。
参考文献:
[深入理解现代浏览器](w3c/20190603_cncuckoo_深入理解现代浏览器.md at master · 75team/w3c · GitHub)
总结:
本笔记主要是阅读[深入理解现代浏览器]这篇文章后所作,通过此篇文章,让我了解了浏览器时如何运行前端开发的代码,也让我对浏览器的架构等有了更深一步的认识。