Menu

html、js、css渲染顺序

浏览器通过http或者本地文件收到http响应数据报文后,根据主体中content-type字段值,判断是否为html文档。为html文档时,新建渲染进程,网络进程和渲染进程通过管道通信共享数据。渲染进程一边从管道获取数据一边进行解析(HtmlParse)。

如果此时遇到外部资源,会再次启动网络接口(http)获取外部资源,对于相应的外部资源会给对应的解析器处理,如js会交给js引擎处理,css会交给CSS解析器处理。

正常情况下, html解析器和css解析器同时进行解析, html解析成一个dom树,解析成dom树的同时,css解析器也会解析成一个css 规则树( CSSOM )。html解析器在遇到脚本文件时,默认会停下来去获取脚本(不考虑资源预加载优化),然后执行,期间阻塞DOM构建。

然后dom树和css规则树生成了附件(attachment),附件形成之后,就可以生成一个渲染树(renderTree)了。

然后渲染树进行layout(布局:计算元素的位置信息,从而进行排版)就可以绘制(Painting)了。Padding过程会遍历Render树的各个节点并调用操作系统的图形接口绘屏,最后通过显示器进行呈现(display)。而页面中图片会在呈现之后再进行加载。

值得注意的是,display是一个渐进的过程。为达到更好的用户体验,display呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。

html、js、css渲染顺序

文件解码成功后会正式开始渲染流程,先会根据 HTML 构建 DOM 树,有 CSS 的话会去构建 CSSOM 树。如果遇到 script 标签的话,会判断是否存在 async 或者 defer ,前者会并行进行下载并执行 JS,后者会先下载文件,然后等待 HTML 解析完成后顺序执行,如果以上都没有,就会阻塞住渲染流程直到 JS 执行完毕。遇到文件下载的会去下载文件,这里如果使用 HTTP 2.0 协议的话会极大的提高多图的下载效率。

初始的 HTML 被完全加载和解析后会触发 DOMContentLoaded 事件

CSSOM 树和 DOM 树构建完成后再通过Layout生成 Render 树,这一步就是确定页面元素的布局、样式等等诸多方面的东西。

在生成 Render 树的过程中,浏览器就开始调用 GPU 绘制,合成图层,将内容显示在屏幕上了

css阻塞

css其实也会阻塞,不要只以为js会阻塞。

CSS会阻塞渲染树的构建,但是不会阻塞DOM构建,但是在CSSOM构建完成之前,页面不会开始渲染(一片空白),会等待css规则树。也就是说css并不会阻塞DOM树的解析(构建),但是会阻塞DOM树渲染。

如果css加载不阻塞DOM树渲染的话,那么当css加载完之后,DOM树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。所以我干脆就先把DOM树的结构先解析完,把可以做的工作做完,然后等你css加载完之后,在根据最终的样式来渲染DOM树,这种做法性能方面确实会比较好一点。

如何优化CSS阻塞

与js不一样,js虽然会阻塞后续DOM构建,但是前面已经就绪的内容会进行渲染。CSS虽然不阻塞DOM构建,但是会阻塞后面js的执行,从而间接阻塞完整DOM的构建。

结论:

  1. css加载不会阻塞DOM树的解析
  2. css加载会阻塞DOM树的渲染
  3. css加载会阻塞后面js语句的执行

js阻塞

JS默认也是会阻塞DOM和渲染树的构建的。

HTML解析器在遇到脚本文件时,默认会停下来去获取脚本(不考虑资源预加载优化),然后执行,期间阻塞DOM构建。

也就是说每当浏览器解析到<script>标签(无论内嵌还是外链)时,浏览器会(一根筋地)优先下载、解析并执行该标签中的javaScript代码,而阻塞了其后所有页面内容的下载和渲染。

所以加载顺序是自上而下,但是渲染流程却是如图.所以最好将js放在最下面。

高版本浏览器上已经实现了脚本并行下载。所以外链多个js文件都是并行下载的(下载也会阻塞),但是其执行顺序依然是顺序执行,阻塞执行。

那么如何避免js阻塞呢?如下文章详细介绍。

js外部脚本异步加载方式

渲染的关键词

  • 关键资源: 可能阻止网页首次渲染的资源。
  • 关键路径长度 获取所有关键资源所需的往返次数或总时间。
  • 关键字节: 实现网页首次渲染所需的总字节数,它是所有关键资源传送文件大小的总和。

进程线程

为什么js加载执行会阻塞渲染树构建

这就得从浏览器来说起来,现在的浏览器都是多进程的浏览器,以谷歌来说,他就是多进程的,每当你打开一个新的tab页的时候,他就会去开一个进程来处理这个tab页面。在这个tab里,是单进程,但是却是多线程的。

脚本加载、脚本执行时都会对其他GUI渲染线程进行阻塞。尤其是在HTML页面初次解析时,它们对性能的影响较大。

  • JavaScript引擎线程
  • GUI渲染线程
  • 事件触发线程
  • 定时触发器线程
  • 异步http请求线程

javascript线程

毫无疑问,这东西就是处理js代码的。例如V8引擎,就是解析运行代码。但是为什么是单线程的呢?因为线程存在上下文切换,可能多个线程操作一个dom,这就产生了资源竞争,索性用了单线程。

GUI线程

这个是负责渲染页面,每当页面重绘的时候这个线程都会调用。js引擎在执行的时候,GUI线程会停止执行,处于挂起的一个状态。

js阻塞GUI引擎

因为js是可以操作dom,GUI也是操作dom, 如果同时运行会存在问题,脚本执行和渲染DOM的并发可能会引发严重的冲突,可能同时修改dom,导致渲染前后不一致。所以js执行时,GUI渲染线程会被挂起。

定时触发器器线程

处理一些异步的web api的时候,碰到定时的,会去处理这种定时,再放入事件队列等待放入栈里。

事件触发线程

一些事件被触发时就会调用该线程,比如一些ui事件,定时事件,ajax请求等,都会放到队列里准备执行。

异步http请求线程

一些http请求连接开启之后,都会新开一个线程去处理。

本文固定连接:https://code.zuifengyun.com/2017/03/2050.html,转载须征得作者授权。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持