共计 4105 个字符,预计需要花费 11 分钟才能阅读完成。
渲染过程的外部工作
这是咱们理解浏览器如何工作 4 篇博客的第 3 篇。之前,咱们介绍了 多过程架构 和 导航流程。在本文中,咱们将钻研渲染过程外部产生了什么。
渲染器过程波及 web 优化的许多方面。因为渲染过程外部产生了很多事件,因而本文只是概述。如果你想更加深刻,“web 基础知识的性能优化局部”有更多的资源。
渲染过程解决 web 内容
渲染过程负责解决 tab 选项卡内产生的所有事件。在一个渲染过程中,主过程解决你发送给用户的大多数代码。如果有时候你应用 web worker 或者 service worker,你的局部 javascript 由 woker 线程解决。排版和光栅线程也在渲染过程外部运行,以便高效,晦涩的渲染页面。
渲染过程的外围工作是将 html、css 和 javascript 转换为能够与用户交互的 web 页面。
解析
DOM 结构
当渲染过程接管到导航提交的信息,并且开始接管 html 数据,主线程开始解析文本字符(html),并且将其转换为文档对象模型(DOM)。
DOM 是一个浏览器对页面的外部示意,也是 web 开发者能够通过 javascript 与之交互的数据结构和 API。
解析一个 HTML 文档到 DOM 是通过 HTML 规范来定义的。你可能留神到将 HTML 放到浏览器素来没有抛出过谬误。例如,短少闭合 </p> 是一个非法的 HTML。谬误的标记例如 Hi! I’m Chrome! (b 标签在 i 标签之前敞开),它被看成是 Hi! I’m Chrome!。 这是因为 HTML 标准定义了如何优雅的解决这些谬误。如果你关怀这些事件是如何实现的,你能够浏览 HTML 标准中“错误处理和解析中奇怪的例子”一节。
子资源加载
一个网站通常会应用额定的资源,例如图片、css 和 javascript。这些文件须要从网络或者缓存中加载。主过程能够 在解析构建 DOM 的时候一个接一个的申请他们,但为了加快速度,“预加载扫描”会同时运行。如果在 HTML 外面有一些像 img 和 link 的内容,预加载会查看 HTML 解析生成的 token,并且在浏览器过程中发送网络申请。
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 局部看到这些信息。
即便你没有提供任何的 CSS,每个 DOM 节点也会又一个 computed 款式。<h1> 标签比 <h2> 标签展现进去大,并且为每个元素都定义了外间距。这是因为浏览器有一个默认样式表。如果你想晓得 chrome 有哪些默认的 css,请查看这个 chrome 默认 css。
布局
当初渲染过程晓得文档的构造和每个节点的款式,然而这些还不足以渲染一个页面。设想一下你在电话外面为你的敌人形容一副画。“这里有一个大红色的圆和一个小的蓝色正方形”这些信息不可能让你的敌人精确的晓得画到底是什么样子。
布局是一个寻找元素坐标的过程。主线程遍历 DOM 和计算款式,创立蕴含 x,y 坐标和边界框大小的布局树。布局树和 DOM 树有着类似的构造,然而它仅仅蕴含页面上可见元素的关联信息。如果利用了 display:none,那么这个元素就不是布局树的一部分(然而,一个 visibility:hidden 的元素在布局树中)。相似地,一个蕴含内容的伪类就像 p::before{content: “Hi!”} 被利用,它会蕴含在布局树中,即便它不在 DOM 中。
确定页面布局是一个有挑战的工作。即便最简略的页面,从上到下块布局,也必须去思考字体多大,哪里须要换行,因为这些都会影响段落的大小和形态;而且也会影响下一行的段落的内容。
CSS 能够使元素浮动到一边,屏蔽溢出项,扭转书写的方向。你能够设想,这个布局阶段有一个艰巨的工作。在 Chrome 中,一个工程师团队都在为布局工作。如果你想理解更多他们的工作细节,请点击 演讲视频 观看乏味的记录。
绘制
有了 DOM,款式,布局还是不足以渲染一个页面。假如你想模拟一幅画。你晓得大小,形态,元素的地位,然而你依然须要判断绘制的程序。
例如,会为一些元素设置 z -index,在上面的例子中,依照 HTML 的程序绘制将会呈现谬误的渲染后果。
图 8:页面元素依照 HTML 标记的程序呈现,因为未思考 z-index 导致生成谬误的渲染图
在以后绘制步骤,主线程遍历布局树去生成绘制记录。绘制记录是对绘制过程的记录,例如“先背景,后文字,最初矩形”。如果你应用 javascript 在 <canvas> 元素上绘制,这个过程对你就比拟相熟。
图 9:主线程遍历布局树并且生成绘制记录
更新渲染流十分低廉
在渲染流中要把握的最重要的一点是,在每一步中,应用上一步操作的后果去生成新数据。例如,如果布局树中的某些产生扭转,须要为受影响的文档局部从新生成绘制程序。
如果为某些元素设置动画,浏览器必须在每个 frame 两头去运行这些操作。咱们大多数的显示器都是每秒钟刷新 60 次屏幕;当你在每一帧中在屏幕上挪动物体时,动画对于人眼的显示都是平滑的。然而,如果错过了它们之间的帧,则页面将显示为“凌乱”。
图 11:时间轴上的动画帧
即便渲染操作域屏幕刷新保持一致,这些计算依然运行在主线程上,这也意味着当你的利用运行 javascript 时会阻塞。
图 12:时间轴上动画帧,但一个帧被 javascript 阻止
你能够将 Javascript 操作分成小块,并应用 requestAnimationFrame() 安顿在每个帧上运行。对于这个话题想要理解更多,你能够看 js 优化执行。你也能够让你 js 运行在 web woker 下来防止阻塞主线程。
图 13:在带有动画帧的时间轴上运行 javascript 小块
合成
你如何去画一个页面?
当初浏览器晓得了文档的构造,每个元素的款式,在页面外面的坐标和绘制程序,如何绘制一个页面?将这些信息转化为屏幕上的像素叫作光栅化(rasterizing)。
解决此问题的一个童稚的办法就是在视口外部栅格化。如果用户滚动页面,则挪动栅格化框架,并通过栅格化更多内容去填充缺失的局部。这是 Chrome 第一次公布时解决栅格化的形式。然而,古代浏览器运行着更为简单的过程,叫作合成。
什么是合成?
合成是将一个页面的各个局部分成多个层,别离对其栅格化的技术,在一个叫作合成线程的独立线程中合称为一个页面。如果产生滚动,因为图层曾经被栅格化,所以就不得不合成一个新的帧。能够应用雷同的形式,通过挪动图层生成一个新的帧来实现动画。
你能够在开发者工具的“Layers panel”中查看你的网站如何被划分为多个图层。
划分为图层
为了找出哪些元素应该在哪些图层,主线程遍历布局树为了生成图层树(在开发者工具中这部分在 performance 面板中被叫作“更新图层树”)。如果页面的某些局部应该是独立的层(例如滑入式侧边栏)没有生成,你能够在 css 中应用 will-change 属性去提醒浏览器。
你可能试图为每个元素提供图层,然而与页面的每帧进行栅格化相比,过多数量的图层合成会使得操作速度变慢,因而掂量应用程序的渲染性能特地重要。想理解更多这个话题,能够查看 Stick to Compositor-Only Properties and Manage Layer Count。
栅格化和合成脱离主线程
一旦图层树生成,并且绘制程序确定,主线程会提交这些信息给合成线程。合成器线程会栅格化每个图层。一个图层能够和整个页面的长度一样大,因而合成器会讲他们划分为图块,并且将每个图块发送给栅格线程。栅格线程栅格化每个图块,并且将他们存储在 GPU 中。
合成器线程会优先解决不同的栅格线程,因而能够首先对视口栅格化。一个图层还具备不同分辨率的平铺,用于解决放大放大的操作。
一旦图块被栅格化,合成器线程将手机信息叫作“draw quads”生成一个“compositor frame”。
draw quads | 蕴含例如图块在内存中地位以及思考页面合成的状况下在页面中合成图块的信息。 |
---|---|
compositor frame | draw quads 汇合去展现一个页面的框架 |
而后合成器框架通过 IPC 提交信息给浏览器过程。此时,能够从更改浏览器 UI 的 UI 线程或者另外一个用于扩大的渲染过程增加另一个合成器框架。这些合成框架都被发送到 GPU 用于显示到屏幕上。如果滚动事件产生,合成器过程创立另一个合成器框架发送给 GPU。
合成的益处是不波及主线程就能够实现。合成器线程不须要等到款式计算和 Js 执行。这是为什么合成动画被认为是最佳的平滑优化的起因。如果布局和绘制须要从新计算,则必须波及主线程。
总结
在这篇文章中,咱们深刻理解了从解析到合成的渲染流。心愿你当初能够获取到更多的对于网站优化的常识。
在上面也是本系列最初一篇文章中,咱们将更加深刻理解合成器线程,并且晓得当用户输出例如 mouse move 和 click 的时候产生了什么。