本文涉及
- CSS 加载阻塞
- JS 加载阻塞
- 阻塞优化
- DOMContentLoaded 事件
- 浏览器渲染步骤及原理
Css 的加载会造成阻塞吗
浏览器渲染过程

- 解析
HTML文件,生成DOM Tree;解析CSS文件,生成CSSOM Tree(这两个过程是并行的) - 将
DOM Tree跟CSSOM Tree结合,生成RenderTree。 - 将
RenderTree渲染到屏幕上。
- 由于解析
HTML跟CSS是并行的,所以css加载不会阻塞 DOM 解析 - 由于
CSSOM Tree跟DOM Tree需要结合,才能生成Render Tree。所以 css 加载会阻塞 DOM 渲染。
那 CSS 加载会阻塞 JS 运行吗?
我理解是可以的,并且实际操作也是可以的。我们已知:js是可以操作DOM树,也可以改变Style。
- 如果
css不阻塞js运行。那RenderTree还为渲染出来时,js 已经并行的运行了,操作了Dom或者Style。那么此时的RenderTree就是旧版本。着就会造成一个结果跟操作不匹配的情况。
那 JS 又会怎么阻塞呢?
JS 不仅会阻塞HTML解析,还会阻塞HTML渲染。
- 原因也很简单,因为 JS 可能会操作
DOM树,如果不阻塞 DOM 的解析,也会造成操作后的DOM树跟已经解析出来的DOM树版本不匹配的情况。
综上,浏览器不允许出现已经解析/渲染出来的内容,跟实际操作过的内容不一致的情况,所以
- CSS 加载会阻塞 DOM 渲染。
- CSS 加载会阻塞 JS 运行。
- JS 加载与执行都会阻塞 DOM 解析与渲染。
这里所有的阻塞,都是指,阻塞下面的代码,因为我们知道浏览器解析代码,是从上到下解析的,如果遇到了 CSS,就会阻塞下面引入的 JS 运行。
多提一嘴优化
CSS 会阻塞 DOM 渲染,所以为了减少用户白屏时间,可以
- 使用 CDN,CDN 会根据你的网络环境,挑选一个最近的缓存内容的节点提供资源,这样减少请求时间。
- CSS 打包压缩,使用打包工具,webpack,glup 等。减少注释与空格等方法,从而减少文件大小减少请求时间。
- 使用缓存。如
Cache-Control,Last-Modified,ETag等操作,增加 Http 缓存。 - 多个 CSS 合并,减少 http 请求。
我们还知道 JS 会阻塞 DOM 解析与渲染,优化这一块可以
- 将 JS 文件放再 HTML 文件最后引入
- 使用
defer或者async延迟加载或异步加载defer会告诉浏览器,不用等待脚本加载。继续加载 HTML 构建 DOM,然后再后台异步加载 js 脚本。具有defer属性的脚本会在文档解析完成开始执行,并且在DOMContentLoaded事件之前执行完成async与defer类似,也是异步加载脚本,但是在加载完成后就会开始执行,不会等到文档解析完成后才开始执行。
1 | <script src="XXXX" defer></script> |
其实还有很多优化方法,但是笔者觉得确实太麻烦了,以上两种最简单方便。所以就只列下这两种哦。
DOMCotentLoaded
DOMContentLoaded是一个事件,当初始 HTML 文档被完全加载和解析完成之后,DOMContentLoaded就会被触发,无需等待样式表、图像、子框架等被完全加载。
跟他相似的是一个我们常接触的事件load。
load 应该仅用于检测一个完全加载的页面 当一个资源及其依赖资源已完成加载时,将触发load事件。
但是要注意涉及到上面阻塞的情况,如果 js 脚本阻塞了 html 的加载,DOMContentLoaded也是不会触发的。所以DOMContentLoaded也有可能在所有脚本执行完毕后触发。
再深入一下!
既然都讲到了客户端对文件加载渲染原理,不如直接把浏览器从 0-1 渲染一个网页讲清楚吧!
浏览器渲染页面的过程可以简单概括为下面几步:
- DNS 查询:通过形如
https://example.com的 url 地址,找到对应的 ip 地址- 初次 DNS 查找,通过域名服务器查找,然后会将 ip 地址缓存一段时间。
- 通过主机名加载一个页面通常仅需要 DNS 查找一次。但是, DNS 需要对不同的页面指向的主机名进行查找。如果
fonts,images,scripts, 等都是不同的主机名,DNS 会对每一个进行查找。
- TCP 连接
- 三次握手
- 四次挥手
- TLS 协商(https 建立安全连接)
- HTTP 请求
- 服务器响应
- 客户端渲染