浏览器原理4:页面渲染

浏览器工具

汇总

image-20220305180056383

网络面板

image-20220305180341429

网络请求时间线

整体流程

image-20220305180642835
Queuing

排队的意思,当浏览器发起一个请求的时候,会有很多原因导致该请求不能被立即执行,而是需要排队等待。比如优先级低低资源如图片,音视频,在遇到css,html,js等高优先级低资源请求是会排队。

HTTP1每个域名维护6个tcp链接,超过数量会排队。http2没有此限制。

网络进程为数据分配磁盘空间时,也要短暂等待磁盘空间分配完成。

Stalled

因为一些其他原因导致链接过程被推迟,就是停滞状态

Proxy Negotiation

如果使用代理服务器,会有代理服务器链接协商阶段

Initial connection/SSL

和服务器建立链接低时间,如果是https,还有个ssl握手时间,用来协商加密信息。

Request sent

和服务器建立好连接之后,网络进程会准备请求数据,并将其发送给网络,这里执行速度很快,只需要把缓存区是数据发送出去就结束了

Waiting (TTFB)

等待接收服务器发来的第一个字节数据。TTFB是反映服务端响应速度的重要指标,对服务器来说,TTFB 时间越短,就说明服务器响应越快

Content Download

进入陆续接收完整数据的阶段,这意味着从第一字节时间到接收到全部响应数据所用的时间。

dom树深入理解

dom树就是把网页的html结构转为浏览器引擎可以理解的内部结构。

  • DOM是生成页面的基础数据结构
  • DOM给js脚步提供操作的接口,通过接口,js可以对DOM树的结构/样式/内容进行修改
  • DOM具有安全功能,一些不安全的内容在DOM解析阶段被拒之门外

DOM树生成原理

HTML解析器(HTMLParser)

它的职责就是负责将HTML字节流转换为DOM结构,HTML解析器并不是等整个文档加载完成之后再解析的,而是网络进程加载了多少数据,HTML解析器便解析多少数据

网络进程下载数据

当网络进程收到相应头后,判断content-type是“text/html”,就会选择或者创建一个渲染进程网络进程和渲染进程之间建立一个共享数据的管道,网络进程向管道中写数据,渲染进程读取数据,把数据传给html解析器,html解析器动态读取字节流,转为DOM。

执行到JavaScript标签时,暂停整个DOM的解析,执行JavaScript代码,不过这里执行JavaScript时,需要先下载这段JavaScript代码。因为JavaScript文件的下载过程会阻塞DOM解析,而通常下载又是非常耗时的。

chrome 的预解析操作,在渲染引擎收到字节流之后,会开启一个预解析线程,用来分析html文件中包含的Javascript 和Css的相关文件,提前下载这些文件,JavaScript文件中没有操作DOM相关代码,就可以将该JavaScript脚本设置为异步加载,通过async 或defer来标记代码。

javascript有可能会操纵CSSOM,所以需要提前下载,解析程CSSOM。而javascript引擎无法确认是否会操纵CSSOM,所以在执行js脚本前,都会执行css下载,解析,在执行js脚本。

html解析数据
image-20220305183821746
  • 词法分析,把字节流转为一个个token,分为tag token和文本token
  • 将token解析为dom节点,token中的节点tag还氛围StartTag和EndTag,他们成对出现。解析器里维护了一个栈结构来处理startTAg和endTag,文本tag不会进入栈中。
  • 将dom节点添加到dom树中。开始时会默认创建一个根为document 的空DOM节点,同时押入栈底,作为根节点。

通过分词器产生的新Token就这样不停地压栈和出栈,整个解析过程就这样一直持续下去,直到分词器将所有字节流分词完成

入栈状态
image-20220305184604320
处栈状态
image-20220305184637984

CSSOM加载过程

只有CSS和HTML的页面加载过程

image-20220305192841549

先是发起主页面的请求,将请求提交到网络进程。网络进程接收到html返回的请求后,发送给渲染进程,渲染进程解析html数据构建DOM,同时开启一个预解析线程,发现有CSS文件,会提前下载CSS文件。后面的合成布局树阶段,需要CSSOM和DOM都准备完成,所以需要css和html都下载完成。

渲染引擎也是无法直接理解CSS文件内容的,所以需要将其解析成渲染引擎能够理解的结构,这个结构就是CSSOM。CSSOM的作用第一个是提供给JavaScript操作样式表的能力,第二个是为布局树的合成提供基础的样式信息

等DOM和CSSOM都构建好之后,渲染引擎就会构造布局树。布局树的结构基本上就是复制DOM树的结构,DOM树中那些不需要显示的元素会被过滤掉(如display:none属性的元素、head标签、script标签等)。复制好基本的布局树结构之后,渲染引擎会为对应的DOM元素选择对应的样式信息,这个过程就是样式计算。样式计算完成之后,渲染引擎还需要计算布局树中每个元素对应的几何位置,这个过程就是计算布局。通过样式计算和计算布局就完成了最终布局树的构建。再之后,就该进行后续的绘制操作了。

CSS,HTML,JS的页面加载过程

image-20220305200623929

和上文不同的是,如果遇到JS脚本,会暂停DOM解析去执行JS脚本的下载解析,而JS脚本与操纵CSS的能力,所以会在JS解析前完成CSS的下载和CSSOM的解析。

页面优化策略

渲染流水线影响到了首次页面展示的速度,而首次页面展示的速度又直接影响到了用户体验

从页面发起请求到首次展示页面内容的三个阶段:

  • 发出请求后,到数据提交阶段,浏览器展示上一个页面的内容。
  • 提交数据之后渲染进程创建空白页面,成为解析白屏,等待CSS文件和JavaScript文件的加载完成,创建CSSOM和DOM,然后合成布局树,最后还要经过一系列的步骤准备首次渲染。
  • 首次渲染完成之后,就开始进入完整页面的生成阶段了,然后页面会一点点被绘制出来。

通常情况下的瓶颈主要体现在下载CSS文件、下载JavaScript文件和执行JavaScript

优化措施

  • 通过内联JavaScript、内联CSS来移除这两种类型的文件下载,在获取HTML文件后就可以构建DOM树
  • 通过webpack等工具压缩JavaScript文件
  • 将一些不需要在解析html阶段使用的JavaScript标记为sync或者defer
  • 对于大的CSS,通过媒体查询属性进行拆分,标记为不同用途的CSS文件

渲染引擎的分层和合成机制

显示图像原理

每个显示器都有固定的刷新频率,通常是60HZ,也就是每秒更新60张图片,更新的图片都来自于显卡中一个叫前缓冲区的地方,显示器所做的任务很简单,就是每秒固定读取60次前缓冲区中的图像,并将读取的图像显示到显示器上。

显卡的职责就是合成新的图像,并将图像保存到后缓冲区中,系统会将后缓冲区和前缓冲区互换,保证显示器能读取最新的图像。然后先看在使用后缓冲区,这就是双缓冲技术。

CPU负责计算数据,把计算好数据交给GPU,GPU会对图形数据进行渲染,渲染好后放到buffer里存起来,然后屏幕或者显示器负责把buffer里的数据呈现到屏幕上.

image-20220305202631490

生成一帧图像

任意一帧的生成方式,有重排、重绘合成三种方式。

重排需要重新根据CSSOM和DOM来计算布局树,然后完整执行渲染流水线。包括分层,绘制列表,格栅化。

image-20220305202913093

重绘

更改元素的背景颜色,不会触发布局阶段,直接进入绘制阶段等子阶段,叫重绘。重绘比重排少了布局和合成阶段,效率高一些

img

直接合成

如果你更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成,如CSS的transform来实现动画效果,在非主线程进行合成动画,合成的效率比前两者高很多。

img

合成操作理解

Chrome中的合成技术,可以用三个词来概括总结:分层、分块合成

以把一张网页想象成是由很多个图片叠加在一起的,每个图片就对应一个图层,合成器最终合成这些不同层的图片。类似Photoshop中的图层合成。将素材分解为多个图层的操作就称为分层,最后将这些图层合并到一起的操作就称为合成。所以,分层和合成通常是一起使用的。

分层体现在生成布局树之后,渲染引擎根据布局树的特点,转为层树(layerTree),层树中的每个节点就是一个图层。分层之后的绘制(paint)就是在不同的图层上,形成绘制指令列表,完成绘制过程,然后就需要进入光栅化阶段了,光栅化就是按照绘制列表中的指令生成图片。每一个图层都对应一张图片,合成线程有了这些图片之后,会将这些图片合成为“一张”图片,然后发给后缓冲区合成操作是在合成线程上完成的,这也就意味着在执行合成操作时,是不会影响到主线程执行的

通常情况下,页面的内容都要比屏幕大得多,合成线程会将每个图层分割为大小固定的图块,然后优先绘制靠近视口的图块。

页面优化

通常一个页面有三个阶段:加载阶段、交互阶段和关闭阶段

  • 加载阶段,是指从发出请求到渲染出完整页面的过程,影响到这个阶段的主要因素有网络和JavaScript脚本。
  • 交互阶段,主要是从页面加载完成到用户交互的整合过程,影响到这个阶段的主要因素是JavaScript脚本。
  • 关闭阶段,主要是用户发出关闭指令后页面所做的一些清理操作。

加载阶段

image-20220305204206224

JavaScript、首次请求的HTML资源文件、CSS文件是会阻塞首次渲染的,因为在构建DOM的过程中需要HTML和JavaScript文件,在构造渲染树的过程中需要用到CSS文件。

优化措施
  • 减少关键资源个数,关键资源越少,网络加载次数就越少
  • 减少关键资源大小,资源越小,下载越快
  • 关键资源需要多少个(RTT,一次网络请求时延)1个HTTP的数据包在14KB左右,

交互阶段

在交互阶段,帧的渲染速度决定了交互的流畅度

image-20220305204555813

一个大的原则就是让单个帧的生成速度变快

优化措施
  • 减少JavaScript脚本执行时间
    • 一种是将一次执行的函数分解为多个任务
    • 采用Web Worker,可以把Web Workers当作主线程之外的一个线程,不过Web Workers中没有DOM、CSSOM环境
  • 避免强制同步布局
    • 重排、重绘合成的耗时逐步递减,尽量用轻量的绘制方式。
  • 避免布局抖动
  • 布局抖动,是指在一次JavaScript执行过程中,多次执行强制布局和抖动操作
  • 合理利用CSS合成动画
    • 合成动画是直接在合成线程上执行的,如果主线程卡顿,合成线程上的动画仍然会执行。
  • 避免频繁的垃圾回收
    • 垃圾回收会产生stoptheword事件,占用主线程,所以避免频繁分配回收短期对象

虚拟DOM

虚拟DOM是最近非常火的技术,两大著名前端框架React和Vue都使用了虚拟DOM。

DOM的缺陷

我们可以调用document.body.appendChild(node)往body节点上添加一个元素,会引发一系列的连锁反应。首先渲染引擎会将node节点添加到body节点之上,然后触发样式计算、布局、绘制、栅格化、合成等任务,这一过程称为重排。还有可能引起重绘或者合成操作。对于DOM的不当操作还有可能引发强制同步布局布局抖动的问题,会大大降低渲染效率。

虚拟DOM

我们需要有一种方式来减少JavaScript对DOM的操作,就是虚拟DOM

虚拟DOM到底要解决哪些事情。

  • 将页面改变的内容应用到虚拟DOM上,而不是直接应用到DOM上。
  • 变化被应用到虚拟DOM上时,虚拟DOM并不立刻渲染页面,而是调整虚拟DOM的内部状态,操作虚拟DOM的代价就变得非常轻了。
  • 在虚拟DOM收集到足够的改变时,再把这些变化一次性应用到真实的DOM上。

简单说就是积累一些列dom的变更,然后一次完成Dom的更新。

image-20220305205440753
  • 创建阶段。首先依据JSX和基础数据创建出来虚拟DOM,它反映了真实的DOM树的结构。然后由虚拟DOM树创建出真实DOM树,真实的DOM树生成完后,再触发渲染流水线往屏幕输出页面。
  • 更新阶段。如果数据发生了改变,那么就需要根据新的数据创建一个新的虚拟DOM树;然后React比较两个树,找出变化的地方,并把变化的地方一次性更新到真实的DOM树上;最后渲染引擎更新渲染流水线,并生成新的页面

可以把虚拟DOM看成是DOM的一个buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到DOM上,这样就能减少一些不必要的更新,同时还能保证DOM的稳定输出。

你可能感兴趣的:(浏览器原理4:页面渲染)