前端渲染过程的二三事

# 前端渲染过程的二三事
本文不会介绍整个前端渲染过程的步骤,只是记录最近阅读的文章的些许思考和感悟。([文章地址一(系列)](https://developers.google.cn/web/fundamentals/performance/critical-rendering-path/),[文章地址二](https://calendar.perfplanet.com/2012/deciphering-the-critical-rendering-path/))

但愿你们在阅读这篇文章以前能将上述文章仔细浏览一篇,由于本文所述基本是基于其内容。

## Navigation Timing API和浏览器加载事件
Navigation Timing API:可经过打印(performance)查看;

浏览器加载事件:domContentLoaded,onload



上图整理了Navigation Timing API中的一些事件和浏览器加载事件的发生顺序。

这些事件的含义是什么呢?咱们直接引用[文章地址一(系列)](https://developers.google.cn/web/fundamentals/performance/critical-rendering-path/measure-crp)的介绍:
- domLoading:这是整个过程的起始时间戳,浏览器即将开始解析第一批收到的 HTML 文档字节。
- domInteractive:表示浏览器完成对全部 HTML 的解析而且 DOM 构建完成的时间点。
- domContentLoaded:表示 DOM 准备就绪而且没有样式表阻止 JavaScript 执行的时间点,这意味着如今咱们能够构建渲染树了。
- 许多 JavaScript 框架都会等待此事件发生后,才开始执行它们本身的逻辑。所以,浏览器会捕获 EventStart 和 EventEnd 时间戳,让咱们可以追踪执行所花费的时间。
- domComplete:顾名思义,全部处理完成,而且网页上的全部资源(图像等)都已下载完毕,也就是说,加载转环已中止旋转。
- loadEvent:做为每一个网页加载的最后一步,浏览器会触发 onload 事件,以便触发额外的应用逻辑。

看了上述事件的介绍,大体了解了每一个事件所表明的含义,但也带着少量疑惑:

1. HTML解析是分批进行的吗?为何要分批进行?
2. domContentLoaded事件结束后能够构建渲染树了,是否意味着CSSOM树构建也在该事件以前就已完成?
3. domContentLoaded事件表示DOM准备就绪而且没有样式表阻止 JavaScript执行的时间点,但是JavaScript执行是在构建DOM树以前,那这是否是意味着该事件在DOM树构建完成后就执行了?如果这与domInteractive事件有什么区别呢?

首先看第一个问题,咱们直接用chrome控制台的performance来看效果。


上图中,上方的Network的蓝条是HTML的下载时间,下方箭头所指是HTML的解析时间。

从中咱们能够得出HTML解析的确是分批进行,而且解析并不须要等HTML彻底下载完。那为何要分批进行呢?这个问题我找了许多资料也没有得出结论,因而本身从中思考。假设不是分批进行,那实现会有两种状况:1.等HTML所有下载完,再一块儿解析;2.每下载必定量的HTML就将其放入解析器等待排队解析。前者首先确定被排除,若HTML很大,会影响首屏加载速度,后者按理速度更快,或许其实现的难度以及可能会带来的一些问题而没有采用?这个问题思考了良久,感受从理论上是可行的,但从技术角度,因为本身这方面的知识实在薄弱,因此也没法得知其实现的过程是否存在技术瓶颈。

第二个问题:CSSOM树构建是否在domContentLoaded事件以前?

话很少说咱们本身实践。

首先我先建立一个HTML文件并写入少量标签,再建立一个CSS并于HTML引入,在CSS文件中写入大量内容(本身实践时写了5W多行)。而后用performance查看。



上图每一个箭头表明的含义:
1. HTML解析结束时间
2. domContentLoaded加载时间
3. CSS文件下载完的时间
4. CSS文件解析的时间

从这个步骤以及其所处时间,咱们能够清晰的得出,该结论不许确。那么该做者为何会得出该结论呢,是他犯错了吗?我随后发现他在这篇文章下面还写了一句“domContentLoaded通常表示DOM和CSSOM均准备就绪的时间点”。那么这句话意味着大部分的时候CSSOM树的构建是在domContentLoaded事件以前。但是这个大部分又指的是什么状况,这又涉及到另外一个知识点“DOM树,CSSOM树,JS的三角关系”,构造DOM树时碰见JS会先解析执行JS,而在解析执行JS时遇到CSSOM,又会先构造CSSOM树,这个过程稍后会具体说明。那么如今咱们能够明白这个问题的关键所在了,由于在大部分页面中是拥有JS的,而因为其解析顺序,那么在domContentLoaded事件以前一定已经成功构造CSSOM树。

第三个问题:domInteractive与domContentLoaded的区别是什么呢?这两个事件中间是否还会进行其余操做?

咱们都知道script标签有defer和async两个属性。有了这两个属性,浏览器就会加一个进程下载JS。那么下载完的执行时间点是在何时?其中async会在JS下载完后立马执行,也正是这个缘由,会致使JS的执行顺序不必定按标签的从上至下,而是按照下载完的时间。那么defer属性的执行时间呢,我想你们应该都能猜到了,它的执行时间点的确就是在上述的两个事件之间,那么咱们也就得知这两个事件的区别所在。

## 三大树

咱们都知道前端渲染有三大树:DOM树,CSSOM树,RENDER树。那么这三大树的构造时间和上述的事件执行时间的顺序又是怎样的呢。

其中DOM树和RENDER树所在位置实际上是显而易见的,而且在以前内容也已经指出。DOM树在domInteractive事件以前,RENDER树在domContentLoaded事件以后。可是CSSOM树就难以捉摸,其与DOM树的关系,彻底受到是否拥有JS影响。

在肯定CSSOM树所处位置前,咱们先肯定一个上面提到的概念:构造DOM树时碰见JS会先解析执行JS,而在解析执行JS时遇到CSSOM,又会先构造CSSOM树。官方给出的缘由是,JS会使用document.write而改变DOM树,因此构造DOM树时碰到JS会先执行JS;而JS在执行时,须要查找CSS,因此执行JS时,碰到构造CSSOM树,会先构造CSSOM树。可是这里有一点奇怪的时,JS也能够经过创造Link标签的方式改变CSSOM树,因此我的感受官方的这种解释有点牵强。不过官方的解释虽然不能让人彻底信服,但这执行顺序是不会有错的。

接下来咱们分别经过有无JS两大类确认CSSOM树所处位置:
1. 无JS的状况
因为DOM树和CSSOM树是并行解析的状况,因此这两个树构建完成的顺序彻底没法固定,只由它们本身自己大小有关。所以CSSOM树构建完成的时间既可能在domInteractive以前,可能在domContentLoaded以后,也可能在这两事件之间。
2. 有JS的状况
这里咱们先假设CSS文件很小,在没还解析到JS时就已完成解析,那么这种状况其实一定发生在domInteractive以前,由于都还没解析到JS,说明DOM树一定没有构建完成。那么再假设CSS文件很大,而后中途遇到JS文件,这时候JS文件发如今构造CSSOM树,其就会等待CSSOM树构建完成后再解析执行JS,因此这种状况CSSOM也一定在domInteractive以前。

可是有2个有意思的状况:
(1) 若是咱们动态建立link标签并添加到html中,那么又会发生什么呢?若JS还没解析执行完,那么会中止JS而去解析CSS,若JS已执行完那么CSSOM树其实也不是一定在domInteractive以前。(固然这可能已经不算初次渲染构建)
(2) link标签放到JS后面又会发生什么呢?这种状况我发现无论我怎么尝试,CSSOM树一定在DOM树构建以前构建,但其又在JS执行完成后。若是有兴趣的同窗能够查查是为何。

## 总结
其实前端渲染是一个很庞大的知识点,而且其涉及的周边知识也及其庞大,本文只是对其中一个小知识点作了思考和实践。最后要说的一点是,以上内容,纯属我的看法,若有不当,请多指教。
相关文章
相关标签/搜索