【译】深入了解现代web浏览器(三)渲染进程内部工作

渲染进程的内部工作

这是我们了解浏览器如何工作4篇博客的第3篇。之前,我们介绍了 多进程架构 和 导航流程。在本文中,我们将研究渲染进程内部发生了什么。

渲染器进程涉及web优化的许多方面。由于渲染进程内部发生了很多事情,因此本文只是概述。如果你想更加深入,“web基础知识的性能优化部分”有更多的资源。

渲染进程处理web内容

渲染进程负责处理tab选项卡内发生的所有事情。在一个渲染进程中,主进程处理你发送给用户的大多数代码。如果有时候你使用web worker 或者 service worker,你的部分 javascript 由woker线程处理。排版和光栅线程也在渲染进程内部运行,以便高效,流畅的渲染页面。

渲染进程的核心工作是将html、css和javascript转换为可以与用户交互的web页面。

renderer.png
renderer.png

解析

DOM构造

当渲染进程接收到导航提交的信息,并且开始接收html数据,主线程开始解析文本字符(html),并且将其转换为文档对象模型(DOM)。

DOM是一个浏览器对页面的内部表示,也是web开发者可以通过javascript与之交互的数据结构和API。

解析一个HTML文档到DOM是通过HTML标准来定义的。你可能注意到将HTML放到浏览器从来没有抛出过错误。例如,缺少闭合 **</p> **是一个合法的HTML。错误的标记例如 **Hi! <b>I'm <i>Chrome</b>!</i> **(b标签在i标签之前关闭),它被看成是 Hi! <b>I'm <i>Chrome</i></b><i>!</i>。这是因为HTML规范定义了如何优雅的处理这些错误。如果你关心这些事情是如何完成的,你可以阅读HTML规范中“错误处理和解析中奇怪的例子”一节。

子资源加载

一个网站通常会使用额外的资源,例如图片、css和javascript。这些文件需要从网络或者缓存中加载。主进程可以 在解析构建DOM的时候一个接一个的请求他们,但为了加快速度,“预加载扫描”会同时运行。如果在HTML里面有一些像img和link的内容,预加载会查看HTML解析生成的token,并且在浏览器进程中发送网络请求。

dom.png
dom.png

javascript会阻塞解析

当HTML解析发现一个script标签,它停止解析HTML文档,并且去加载,解析,并且执行javascript代码。为什么?因为javascript可以改变文档的形状,使用document.write()可以改变整个的DOM结构(解析模型概述在HTML定义里有一个好的绘图)。这是HTML解析为什么在恢复HTML文档解析之前等待javascript执行。如果你像深入了解javascript执行发生了什么,V8团队关于此的讨论在这。

提示浏览器如何加载资源

web开发者有很多种方式可以发送提示给浏览器按序加载资源。如果你的javascript没有使用document.write(),你可以添加 async 和 defer 属性给 <script> 标签。浏览器可以异步地加载和执行 javascript 代码,并且不会阻塞html解析。如果适合的话你可能也会使用 javascript module。<link rel="preload">是一种通知浏览器在当前导航需要尽可能早的下载资源的方式。你可以阅读更多的内容在这里 Resource Prioritization – Getting the Browser to Help You.

样式计算

只有一个DOM是不足以知道页面长什么样子的,因为我们可以在css中设置页面样式。主线程解析CSS并且为每个DOM节点准确地计算出样式。这是基于CSS选择器为每个元素应用对应样式的信息。你可以在 DevTools 中的 computed 部分看到这些信息。

computedstyle.png
computedstyle.png

即使你没有提供任何的CSS,每个DOM节点也会又一个computed样式。<h1>标签比<h2>标签展示出来大,并且为每个元素都定义了外间距。这是因为浏览器有一个默认样式表。如果你想知道chrome有哪些默认的css,请查看这个chrome默认css

布局

现在渲染进程知道文档的结构和每个节点的样式,但是这些还不足以渲染一个页面。想象一下你在电话里面为你的朋友描述一副画。“这里有一个大红色的圆和一个小的蓝色正方形”这些信息不能够让你的朋友准确的知道画到底是什么样子。

tellgame.png
tellgame.png

布局是一个寻找元素坐标的过程。主线程遍历DOM和计算样式,创建包含x,y坐标和边界框大小的布局树。布局树和DOM树有着相似的结构,但是它仅仅包含页面上可见元素的关联信息。如果应用了 display:none,那么这个元素就不是布局树的一部分(然而,一个visibility:hidden的元素在布局树中)。类似地,一个包含内容的伪类就像p::before{content: "Hi!"}被应用,它会包含在布局树中,即使它不在DOM中。

layout.png
layout.png

确定页面布局是一个有挑战的任务。即使最简单的页面,从上到下块布局,也必须去考虑字体多大,哪里需要换行,因为这些都会影响段落的大小和形状;而且也会影响下一行的段落的内容。

CSS可以使元素浮动到一边,屏蔽溢出项,改变书写的方向。你可以想象,这个布局阶段有一个艰巨的任务。在Chrome中,一个工程师团队都在为布局工作。如果你想了解更多他们的工作细节,请点击 演讲视频 观看有趣的记录。

绘制

有了DOM,样式,布局还是不足以渲染一个页面。假设你想模仿一幅画。你知道大小,形状,元素的位置,但是你仍然需要判断绘制的顺序。

drawgame.png
drawgame.png

例如,会为一些元素设置z-index,在下面的例子中,按照HTML的顺序绘制将会出现错误的渲染结果。

zindex.png
zindex.png

图8:页面元素按照HTML标记的顺序出现,由于未考虑 z-index导致生成错误的渲染图

在当前绘制步骤,主线程遍历布局树去生成绘制记录。绘制记录是对绘制过程的记录,例如“先背景,后文字,最后矩形”。如果你使用 javascript 在<canvas>元素上绘制,这个过程对你就比较熟悉。


paint.png
paint.png

图9:主线程遍历布局树并且生成绘制记录

更新渲染流非常昂贵

在渲染流中要掌握的最重要的一点是,在每一步中,使用上一步操作的结果去生成新数据。例如,如果布局树中的某些发生改变,需要为受影响的文档部分重新生成绘制顺序。

如果为某些元素设置动画,浏览器必须在每个frame中间去运行这些操作。我们大多数的显示器都是每秒钟刷新60次屏幕;当你在每一帧中在屏幕上移动物体时,动画对于人眼的显示都是平滑的。然而,如果错过了它们之间的帧,则页面将显示为“混乱”。


pagejank1.png
pagejank1.png

图11:时间轴上的动画帧

即使渲染操作域屏幕刷新保持一致,这些计算仍然运行在主线程上,这也意味着当你的应用运行javascript时会阻塞。


pagejank2.png
pagejank2.png

图12:时间轴上动画帧,但一个帧被javascript阻止

你可以将Javascript操作分成小块,并使用requestAnimationFrame()安排在每个帧上运行。对于这个话题想要了解更多,你可以看 js优化执行。你也可以让你js运行在web woker上去避免阻塞主线程。

raf.png
raf.png

图13:在带有动画帧的时间轴上运行javascript小块

合成

你如何去画一个页面?

现在浏览器知道了文档的结构,每个元素的样式,在页面里面的坐标和绘制顺序,如何绘制一个页面?将这些信息转化为屏幕上的像素叫作光栅化(rasterizing)。

处理此问题的一个幼稚的方法就是在视口内部栅格化。如果用户滚动页面,则移动栅格化框架,并通过栅格化更多内容去填充缺失的部分。这是Chrome第一次发布时处理栅格化的方式。然而,现代浏览器运行着更为复杂的过程,叫作合成。

什么是合成?

合成是将一个页面的各个部分分成多个层,分别对其栅格化的技术,在一个叫作合成线程的独立线程中合称为一个页面。如果发生滚动,由于图层已经被栅格化,所以就不得不合成一个新的帧。可以使用相同的方式,通过移动图层生成一个新的帧来实现动画。

你可以在开发者工具的“Layers panel”中查看你的网站如何被划分为多个图层。

划分为图层

为了找出哪些元素应该在哪些图层,主线程遍历布局树为了生成图层树(在开发者工具中这部分在performance面板中被叫作“更新图层树”)。如果页面的某些部分应该是独立的层(例如滑入式侧边栏)没有生成,你可以在css中使用 will-change 属性去提示浏览器。

layer.png
layer.png

你可能试图为每个元素提供图层,但是与页面的每帧进行栅格化相比,过多数量的图层合成会使得操作速度变慢,因此衡量应用程序的渲染性能特别重要。想了解更多这个话题,可以查看 Stick to Compositor-Only Properties and Manage Layer Count

栅格化和合成脱离主线程

一旦图层树生成,并且绘制顺序确定,主线程会提交这些信息给合成线程。合成器线程会栅格化每个图层。一个图层可以和整个页面的长度一样大,因此合成器会讲他们划分为图块,并且将每个图块发送给栅格线程。栅格线程栅格化每个图块,并且将他们存储在GPU中。

raster.png
raster.png

合成器线程会优先处理不同的栅格线程,因此可以首先对视口栅格化。一个图层还具有不同分辨率的平铺,用于处理放大缩小的操作。

一旦图块被栅格化,合成器线程将手机信息叫作“draw quads”生成一个“compositor frame”。

draw quads 包含例如图块在内存中位置以及考虑页面合成的情况下在页面中合成图块的信息。
compositor frame draw quads集合去展示一个页面的框架

然后合成器框架通过IPC提交信息给浏览器进程。此时,可以从更改浏览器UI的UI线程或者另外一个用于扩展的渲染进程添加另一个合成器框架。这些合成框架都被发送到GPU用于显示到屏幕上。如果滚动事件发生,合成器进程创建另一个合成器框架发送给GPU。

composit.png
composit.png

合成的好处是不涉及主线程就可以完成。合成器线程不需要等到样式计算和Js执行。这是为什么合成动画被认为是最佳的平滑优化的原因。如果布局和绘制需要重新计算,则必须涉及主线程。

总结

在这篇文章中,我们深入了解了从解析到合成的渲染流。希望你现在可以获取到更多的关于网站优化的知识。

在下面也是本系列最后一篇文章中,我们将更加深入了解合成器线程,并且知道当用户输入例如mouse move和click的时候发生了什么。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容

  • 浏览器的渲染过程 在我们面试过程中,经常会遇到面试官问我们,当我们从浏览器地址栏输入URL之后到页面显示,浏览器到...
    达文西_Huong阅读 858评论 0 0
  • 1.浏览器的发展史 1992年,托尼哟翰逊(Tony Johnson)发布了Midas,它允许用户浏览UNIX和V...
    不会飞的fish阅读 529评论 0 0
  • 浏览器组成 用户界面 :展示除标签页窗口之外的其他用户界面内容 浏览器引擎:在用户界面和渲染引擎之间传递数据,具有...
    杉绿阅读 169评论 0 1
  • 浏览器架构 在讲浏览器架构之前,先理解两个概念,进程和线程。 进程(process)是程序的一次执行过程,是一个动...
    淺時咣阅读 358评论 0 2
  • 一、浏览器如何渲染网页 要了解浏览器渲染页面的过程,首先得知道一个名词——关键路径渲染。关键渲染路径(Critic...
    Srtian阅读 995评论 1 3