本文涉及
- 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 请求
- 服务器响应
- 客户端渲染