请你描述下一个网页是如何渲染出来的?哪些会造成渲染阻塞?
解析渲染
浏览器边加载html边构建DOM树,当然会有容错和修正机制。浏览器解析到行内css和内联css会立马加入到构建渲染树,解析到外链css就开始加载,加载完之后也会合并到渲染树的构建中,最后将渲染树和DOM做节点链路匹配,也叫layout阶段,计算每个DOM元素最终在屏幕上显示的大小和位置。 遍历顺序为从左至右,从上到下,绘制在屏幕上,layout过程中可能会触发页面回流和重绘,比如某个外链css加载完解析之后合并构建到渲染树中,其中某个css改变了DOM树种某个元素的定位(改成绝对定位)或者改变了长宽边距等位置信息,会触发重新layout,所以会回流reflow。重绘是比如css改变了之前的背景图片颜色,浏览器会重新绘制。
渲染阻塞
会有渲染阻塞,浏览器刷新的频率大概是60次/秒, 也就是说刷新一次大概时间为16ms,如果浏览器对每一帧的渲染工作超过了这个时间, 页面的渲染就会出现卡顿的现象。浏览器内核中有3个最主要的线程:JS线程,UI渲染线程,事件处理线程。此外还有http网络线程,定时器任务线程,文件系统处理线程等等。
- JS线程负责JS代码解析编译执行,称为主线程。常说‘浏览器是单线程’指的是JS主线程只能有一个,主线程执行同步任务,会阻塞UI渲染线程。JS线程核心是js引擎 (IE9+: Chakra firefox:monkey chrome:v8)。webworker可以创建多个js线程,但是受主线程控制,主要用于cpu密集型计算。
- UI渲染线程当然是负责构建渲染树,执行页面元素渲染。核心是渲染引擎(firefox:gecko、chrome/safari:webkit),由于JS可以操作DOM元素处理样式等,JS主线程是执行同步任务的,所以设计上JS引擎线程和GUI渲染线程是互斥的。 也就是说JS引擎处于运行状态时,GUI渲染线程将处于冻结状态。
- 事件处理线程,由于浏览器是事件驱动的,事件处理线程用来控制事件回调处理,浏览器触发某个事件后会把事件回调函数放到任务队列中,可以看下下面会提到。
- 其他线程统称工作线程,如处理 ajax 的线程,dom事件线程、定时器线程、读写文件的线程等,工作线程的任务完成之后, 会推入到一个任务队列(task queue)
总结一下,渲染阻塞有三个方面:
- css解析会阻塞页面渲染,css文件太大或者选择器效率低,也会暂用较长解析时间
- js主线程执行时间长会导致渲染线程阻塞,影响渲染。我们也称为longtask
- 渲染线程自身阻塞,渲染时间达不到帧率60,会看起来卡顿,比如回流或者重绘等,或者css效率太低,动画处理不合适,导致渲染耗时