从页⾯加载到⾸屏渲染时机
序⾔
随着⽤户量越来越多,业务⽅关于⽤户体验的要求也在不断提⾼,⾸屏渲染时间就成为了⼀个提⾼⽤户体验的指标,减少⽤户等待的时间,在⼀定程度上就会提⾼⽤户的留存。
页⾯加载渲染是怎样的⼀个过程
参考⾃
1. 浏览器输⼊url,浏览器发送请求到服务器,服务器将请求的HTML返回给浏览器。
2. 浏览器下载完成HTML(Finish Loading HTML)之后,便开始从上到下解析。
3. 解析的过程中碰到css和js外链(其实HTML的下载也是这个流程)都会执⾏以下过程:
Send Request:表⽰给这个外链对应的服务器发送请求
Receive Response: 表⽰接收响应,这⾥是表⽰告诉浏览器可以开始从⽹络接收数据了
Receive Data:表⽰开始接收数据
Finish Loading: 表⽰已经完成下载数据。
Parse Stylesheet/Evaluate(默认情况下js下载完成之后执⾏Evaluate,css下载完成后会进⾏Parse Stylesheet)
4. 所有的css下载完成后Parse Stylesheet然后开始构建CSSOM DOM(⽂档对象模型)和 CSSOM(CSS对象模型)会合并⽣成⼀个渲
染树(Render Tree)
5. 根据渲染树的内容计算处各个节点在⽹页中的⼤⼩和位置(Layout,可以理解为“刻章”)
6. 根据Layout绘制内容在浏览器上(Paint,可以理解为“盖章”)。
页⾯加载渲染
通过这个过程我们可以看到,整个页⾯加载到渲染其实可以分为两个部分,第⼀部分是资源的的加载,简称为RRDL;第⼆部分是页⾯的渲染。
探讨⾸屏渲染,我们也将从这两个⽅⾯⼊⼿来讨论。
⾸屏渲染是什么时候开始呢?
参考⾃
先看⼀个结论,触发⾸屏渲染的时机,会有两种情况:
1. 如果第⼀脚本前的JS和CSS加载完了,body中的脚本还未下载完成,那么浏览器就会利⽤构建好的局部CSSOM和DOM提前渲染第⼀
脚本前的内容(触发FP);
2. 如果第⼀脚本前的JS和CSS都还没下载完成,body中的脚本就已经下载完了,那么浏览器就会在所有JS脚本都执⾏完之后才触发
FP。
第⼀脚本:body中的第⼀个外链脚本。
所以,我们需要按照第⼀条的⽅式来安排页⾯的内容,争取在第⼀脚本前JS和CSS要尽快加载完,然后尽快进⼊⾸屏渲染。
如何加快⾸屏渲染?
参考⾃
CSS放在head中,JS放在</body>前(如果在head必须放JS,也尽量减少他的⼤⼩,把⼤JS⽂件放</body>前)。
减⼩head中CSS和JS⼤⼩(gzip了解⼀下?)
优化head中的JS和CSS外链的⽹络情况,减少Stalled、TTFB和Content Download的时间。
在第⼀脚本前使⽤⾻架图,可以减少⽤户的⽩屏感知时间(对于使⽤JS插⼊模板来渲染的框架,建议将⾻架图的路由⽣成逻辑单独提出来)
整个页⾯的⽣命周期是怎样的呢?
参考⾃
HTML页⾯的⽣命周期有以下以下⼏个重要事件:
DOMContentLoaded :浏览器已经完全加载了 HTML,DOM 树已经构建完毕,但是像是<img>和样式
表等外部资源可能并没有下载完毕。
load:浏览器已经加载了所有的资源(图像,样式表等)。
beforeunload/unload :当⽤户离开页⾯的时候触发。
注册页面js特效readyState :描述document的loading状态,其实是对页⾯加载事件的细化,⽐如loading表⽰DOMContentLoaded还没有触发,我们在这个时候注册DOMContentLoaded事件是有效的。interactive后会触发DOMContentLoaded,comoplete之后会触发load事件。
readyState的改变会触发readystatechange事件。
loading 加载:document仍在加载。
interactive 互动:⽂档已经完成加载,⽂档已被解析,但是诸如图像,样式表和框架之类的⼦资源仍在加载。
complete :⽂档和所有⼦资源已完成加载。状态表⽰ load 事件即将被触发。
并⾏下载,串⾏执⾏
页⾯依赖外部资源下载是并⾏的,但是DOM树解析、js执⾏和⾸屏渲染却是串⾏的。理解整个过程,就能明⽩为什么Head中不要放JS的原因,以及CSS为什么要尽量⼩。
页⾯渲染的⼤致过程为,先下载解析HTML并建⽴DOM树,再解析css绘制渲染树。前者搭建页⾯结构,后者增添页⾯样式。⽽在建⽴DOM 树的过程就会遇到诸如img、外联css和script标签,此时就要加载外部资源了。加载资源是由单独的下载线程进⾏异步加载的,浏览器会并⾏加载,不过具体并⾏最⼤数量是有⼀定限制的,不同浏览器可能不⼀样。
但是加载css和js资源⽐较特殊,它们的加载会影响页⾯渲染。css加载不会阻塞DOM树解析,但会阻塞渲染(这是由于渲染依赖于css,如果不等css加载完就渲染的话那么等css加载解析完⼜得重新渲染,可能⼜要重绘或者回流)。对于js资源的加载,则会阻塞DOM树的构建和渲染,除⾮设置了script标签的异步属性。
放在head中会在解析DOM树和渲染页⾯前就加载,并阻塞页⾯。js正常情况下加载完就会⽴即执⾏,在js脚本中只能访问当前<script>以上的DOM,脚本执⾏结束后再继续解析DOM。js执⾏引擎和页⾯渲染是由不同的线程来执⾏,但这两者是互斥的,也就是说js执⾏过程是⽆法构建DOM和渲染页⾯的。这是⼀种优化机制,由于js可能会对DOM及样式进⾏修改,如果解析js过程中同时构建DOM,就可能造成前后内容不⼀致或者重复构建。所以应该把script放在body中,使页⾯更快完成渲染。
⼀些常识
参考⾃
Chrome会渲染局部CSSOM和DOM。整个特别重要,理解这个概念,才能理解为什么不需要DOMContentLoaded,就可以进⾏⾸屏渲染了,不然总是会误解,⾸屏渲染必须在DOMContentLoaded之后。
First Paint和DOMContentLoaded、load事件的触发没有绝对的关系,FP可能在他们之前,也可能在他们之后,这取决于影响他们触发的因素的各⾃时间(FP:第⼀脚本前CSSOM和DOM的构建速度;DOMContentLoaded:HTML⽂档⾃⾝以及HTML⽂档中所有JS、CSS的加载速度;load:图⽚、⾳频、视频、字体的加载速度)。
DOMContentLoaded和load事件也没有强制的先后顺序,DOMContentLoaded⼀般在load事件之前触发,但也可能在load事件之后触发。
第⼀脚本前的CSS如果还会去加载字体⽂件,那么即使CSSOM和DOM构建完成触发FP,页⾯内容也会是空⽩,只有等到字体⽂件下载完成才会出现内容(这也是我们在打开⼀个加载了⾕歌字体的⽹站会⽩屏很长时间的原因)。
默认情况下,CSS外链之间是谁先加载完成谁先解析,但是JS外链之间即使先加载完成,也得按顺序执⾏。
link外链后⾯紧跟script外链,须先等link parse完成之后,script才会执⾏,即使script先下载完成。script后⾯紧跟link,也是⼀样,会等script执⾏完之后,link才会parse。
如果script之后紧跟⼏个link且script⽐这⼏个link的下载时间都长,那script执⾏完成之后link是按顺序执⾏。
RRDL:
R:send Request,发送资源请求
R:receive Response,接收到服务端响应
D:receive Data,开始接受服务端数据(⼀个资源可能执⾏多次)
L:finish Loading,完成资源下载
浏览器在RRDL的时候,在D(Receive data)这个步骤可能执⾏多次。
TTFB:Time To First Byte,第⼀个字节返回的时间,这个是对应send Request到receive Response这段时间。
浏览器会给HTML中的资源⽂件进⾏等级分类(Hightest/High/Meduim/Low/Lowest),⼀般HTML⽂档⾃⾝、head中的CSS都是Hightest,head中JS⼀般是High,⽽图⽚⼀般是Low,⽽设置了async/defer的脚本⼀般是Low,gif图⽚⼀般是Lowest。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论