浏览器是如何将HTML、JS、CSS、image等资源渲染成可视化的页面的呢?本文简单介绍一下渲染过程当中涉及到的关键步骤。css
该过程分为四步:模型对象的构建、渲染树构建、布局、绘制。html
浏览器获取到HTML、CSS文件后,须要对其进行解析,抽象成DOM和CSSOM对象,而后提供相应的JS API,方便开发者进行交互逻辑开发。前端
HTML文件字节转变成DOM的过程以下图所示:web
主要经历字符编码—》令牌提取标签—》词法分析转变成DOM对象—》依照标签的嵌套关系构建成DOM树;chrome
CSS文件字节转变成CSSOM的过程与HTML转DOM相似,区别就是按照规则通用性创建树形关系。浏览器
所谓渲染树,就是将DOM树和CSSOM树合并,获得每一个可见元素的内容和显示样式。缓存
Tips:性能优化
(1)渲染树并不是显示全部元素,而只是占据空间元素,如display: none的元素不在渲染树中,而visibility: hidden的在渲染树中;网络
(2)渲染树包含的内容只是元素的内容及其样式信息,在不一样视口(viewport,也就是浏览器的屏幕画布)下实际展现肯能会有差异;dom
(3)渲染树构建后,Webkit还会继续构建渲染层(RenderLayer),这是为了简化渲染逻辑,同时方便开发者查看网页层次。
通过前两步的操做,咱们知道了元素的内容和样式信息,可是实际在不一样显示器中的大小和位置如何肯定呢,这就须要进行布局操做了,有的地方称为"自动重排"(reflow)。Webkit依据框模型来计算元素的位置和大小,布局输出的是一个"盒模型"对象,该对象包含了每一个元素在视口内的确切位置和尺寸。
在布局结束后,接下来就是绘制,实现栅格化。绘制通常涉及到Paint和Composite Layers。
Paint通常经过图像上下文来控制,分为2D和3D绘制上下文。
前文提到了RenderLayer的概念,绘制过程当中,每一个RenderLayer是输出图像中的一层,各个层根据深度信息组合成一张图像,这个组合的过程称为Composite Layers。
介绍完了关键渲染路径的概念,接下来结合chrome dev-tool来看一下实际的状况,chrome的版本是60.0。
<html> <head> <title>Janky Animation</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="index.css"/> <link rel="stylesheet" type="text/css" href="https://ss1.bdstatic.com/ 5eN1bjq8AAUYm2zgoY3K/r/www/cache/static/protocol/https/soutu/css/soutu.css"/> </head> <body> <img class="proto mover" src="./images/logo-1024px.png"/> <div class="controls"> <button class="add"></button> <button class="subtract" disabled></button> <button class="stop">Stop</button> <button class="optimize">Optimize</button> <button class="optimize-fastdom">Optimize By Fastdom</button> <a href="https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/" target="_blank"> <button class="optimize">Help</button> </a> </div> <script src="./libs/fastdom.js"></script> <script src="index.js"></script> </body> </html>
为了不chrome插件的干扰,建议使用【隐身窗口】打开页面,而后打开dev-tool,选择Performance进行调试
渲染的关键路径主要体如今主线程中,以下图所示。
图中的蓝色的Parse HTML表示DOM的构建过程,蓝色的Parse StyleSheet表明CSSOM的构建过程,黄色的Evaluate Script表示JS的执行过程,紫色的Recalculate Style表示构建Render Tree的过程,紫色的Layout表示布局过程。
虽然资源的下载能够并行,可是资源的解析是单线程的,主要经过Main线程来解析,由下图所示,ParseHTML被JS的解析阻塞,分红了三段。线程的使用状况和代码中的资源的位置有很大关系,这个下面会介绍。
Main线程中的图中,有一些细线条记录着一些事件的触发时间,光标放在上面就能够查看。事件主要分为Loading、Scripting、Redering、Painting四大类,具体能够查看官方介绍。其中Scripting类型中有一种Event类型的事件,以下图中的Event(DOMCotentLoaded)能够在JS中被监听到,经常使用的还有readystatechange、pageshow、pagehide、loaded、webkitvisiblechange等,最近有一个项目中pageshow事件就帮我解决了IOS WKWebview回退页面缓存不刷新的问题。
一般咱们会将css文件放在head标签中,JS文件放置在body标签的后面,这是有必定道理的。因为JS执行的过程当中可能修改DOM和CSS样式,这也就形成了Evaluate Script的执行会阻塞Parse HTML的过程,若是JS中引用未解析到的DOM程序就会报错;若是script标签以前有引入css文件,Evaluate Script会等到Parse Stylesheet过程结束后再执行。
因此将CSS文件放置在头部,提早下载并解析;将JS文件放在尾部,让JS尽量的访问到全部的DOM,避免报错。
前端性能优化主要分为网络请求和代码层面两种。网络请求上的方法是压缩合并、按需加载、缓存等;代码层面则就是要优化渲染路径,毕竟单线程要在模型对象构建、渲染树构建、布局、渲染之间切换,以下图所示。
优化渲染路径对于页面性能相当重要,接下来会写几篇文章针对不一样阶段给出优化方法,敬请期待。