浏览器工具
汇总
网络面板
网络请求时间线
整体流程
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解析数据
- 词法分析,把字节流转为一个个token,分为tag token和文本token
- 将token解析为dom节点,token中的节点tag还氛围StartTag和EndTag,他们成对出现。解析器里维护了一个栈结构来处理startTAg和endTag,文本tag不会进入栈中。
- 将dom节点添加到dom树中。开始时会默认创建一个根为document 的空DOM节点,同时押入栈底,作为根节点。
通过分词器产生的新Token就这样不停地压栈和出栈,整个解析过程就这样一直持续下去,直到分词器将所有字节流分词完成
入栈状态
处栈状态
CSSOM加载过程
只有CSS和HTML的页面加载过程
先是发起主页面的请求,将请求提交到网络进程。网络进程接收到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的页面加载过程
和上文不同的是,如果遇到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里的数据呈现到屏幕上.
生成一帧图像
任意一帧的生成方式,有重排、重绘和合成三种方式。
重排需要重新根据CSSOM和DOM来计算布局树,然后完整执行渲染流水线。包括分层,绘制列表,格栅化。
重绘
更改元素的背景颜色,不会触发布局阶段,直接进入绘制阶段等子阶段,叫重绘。重绘比重排少了布局和合成阶段,效率高一些
直接合成
如果你更改一个既不要布局也不要绘制的属性,渲染引擎将跳过布局和绘制,只执行后续的合成操作,我们把这个过程叫做合成,如CSS的transform来实现动画效果,在非主线程进行合成动画,合成的效率比前两者高很多。
合成操作理解
Chrome中的合成技术,可以用三个词来概括总结:分层、分块和合成。
以把一张网页想象成是由很多个图片叠加在一起的,每个图片就对应一个图层,合成器最终合成这些不同层的图片。类似Photoshop中的图层合成。将素材分解为多个图层的操作就称为分层,最后将这些图层合并到一起的操作就称为合成。所以,分层和合成通常是一起使用的。
分层体现在生成布局树之后,渲染引擎根据布局树的特点,转为层树(layerTree),层树中的每个节点就是一个图层。分层之后的绘制(paint)就是在不同的图层上,形成绘制指令列表,完成绘制过程,然后就需要进入光栅化阶段了,光栅化就是按照绘制列表中的指令生成图片。每一个图层都对应一张图片,合成线程有了这些图片之后,会将这些图片合成为“一张”图片,然后发给后缓冲区。合成操作是在合成线程上完成的,这也就意味着在执行合成操作时,是不会影响到主线程执行的。
通常情况下,页面的内容都要比屏幕大得多,合成线程会将每个图层分割为大小固定的图块,然后优先绘制靠近视口的图块。
页面优化
通常一个页面有三个阶段:加载阶段、交互阶段和关闭阶段。
- 加载阶段,是指从发出请求到渲染出完整页面的过程,影响到这个阶段的主要因素有网络和JavaScript脚本。
- 交互阶段,主要是从页面加载完成到用户交互的整合过程,影响到这个阶段的主要因素是JavaScript脚本。
- 关闭阶段,主要是用户发出关闭指令后页面所做的一些清理操作。
加载阶段
JavaScript、首次请求的HTML资源文件、CSS文件是会阻塞首次渲染的,因为在构建DOM的过程中需要HTML和JavaScript文件,在构造渲染树的过程中需要用到CSS文件。
优化措施
- 减少关键资源个数,关键资源越少,网络加载次数就越少
- 减少关键资源大小,资源越小,下载越快
- 关键资源需要多少个(RTT,一次网络请求时延)1个HTTP的数据包在14KB左右,
交互阶段
在交互阶段,帧的渲染速度决定了交互的流畅度
一个大的原则就是让单个帧的生成速度变快
优化措施
- 减少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的更新。
- 创建阶段。首先依据JSX和基础数据创建出来虚拟DOM,它反映了真实的DOM树的结构。然后由虚拟DOM树创建出真实DOM树,真实的DOM树生成完后,再触发渲染流水线往屏幕输出页面。
- 更新阶段。如果数据发生了改变,那么就需要根据新的数据创建一个新的虚拟DOM树;然后React比较两个树,找出变化的地方,并把变化的地方一次性更新到真实的DOM树上;最后渲染引擎更新渲染流水线,并生成新的页面
可以把虚拟DOM看成是DOM的一个buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到DOM上,这样就能减少一些不必要的更新,同时还能保证DOM的稳定输出。