页面加载时会被 JS 和 CSS 阻塞吗?

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 之前写了两篇文章,涉及到了页面访问整个过程的一些分析,比如页面生命周期的介绍,页面访问时渲染过程中 HTML、JS 的关系,前面两篇只是抓住了 JS,没有囊括 CSS,并且在复现上没

之前写了两篇文章,涉及到了页面访问整个过程的一些分析,比如页面生命周期的介绍页面访问时渲染过程中 HTML、JS 的关系,前面两篇只是抓住了 JS,没有囊括 CSS,并且在复现上没有明确给出工具,而今天这篇文章将使用 Chrome 的 Network 和 Performance 工具去分析整个页面的加载过程

  1. Network: 分析请求,文章中用于分析请求发送时序关系
  2. Performance: 分析页面加载性能,用于查看 HTML, CSS, JS 解析时序

前排提示,使用上面两种工具去分析时,请打开无痕模式并且关掉在无痕模式允许运行的插件,目的是为了避免插件脚本的加载影响后期的分析

CSS

就目前类似的文章中,都是在头部导入采用的延时去模拟 CSS 可能会阻塞 DOM 解析的效果,对于 CSS 这类静态资源来说,它们是由专门的下载线程来下载的,不会阻塞 GUI 线程(HTML 解析所在线程),但是 <link><script> 是同步请求,就是需要发送请求后需要等待响应,那么阻塞 HTML 的真正原因可能是 1. 同步请求因为延迟导致的阻塞,等返回响应后 HTML 就会解析 2. HTML 遇到头部的外联 CSS 样式后,文件太大导致的阻塞

为了搞清楚这两个点,我分别设置了延时和大 CSS 文件两种模拟方式,代码如下

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="http://localhost:8000/500.css" />
  </head>

  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="http://localhost:8000/big.css" />
  </head>

  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
</html>

延时模式下

img4.png

大文件模式下

img3.png

仔细看上面两个图蓝色虚线,也就是 DCL 所在的位置,全部都是在 CSS 加载开始一点点就完成了,对于上面的假设

  1. 同步请求阻塞
  2. 大文件阻塞

都没有影响到 HTML 的解析(注:外联样式 CSS 的请求实际上是在 Parse HTML 之前发生的)

由上面我们也可以知道,延时阻塞和大文件阻塞起到的效果是一致的,因此接下来的 demo 中都会采用延时的方案

由上我们可以知道,位于头部(头部一般指 <head> 中) CSS 不会阻塞 DOM 解析(头部是一个伏笔, 先不讲)

位于头部的 CSS 会阻塞页面渲染

页面加载的过程中实际上是一行行读取 HTML,因此页面的内容也是一点点的打印/渲染出来,所以就有了 First Plain 的出现,含义为页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间,以今日头条的访问为例,当页面还在转圈时,页面内容其实就已经能够被看到大部分了,之所以还在转圈是因为图片等资源还没有下载完成,而根据白屏时间的定义不就是 First Plain 之前的时间吗?

CSS 阻塞页面渲染,为了避免页面重复渲染(比如 红 -> 绿 -> 白 -> ...),页面会等待 CSS 的下载完成,前面测试了延时和大文件 CSS 的加载,两张图中红色部分为 load,绿色为 First Plain,即页面第一次渲染,由此可见在头部的 CSS 阻塞页面的渲染,不会阻塞 DOM

凡事有例外 - FOUC

<link> 被置于某些元素的中间,就会阻塞 dom 的解析了,看看下面的例子

<!DOCTYPE html>
<html>
  <body>
    <p>究竟什么时候解析到我呢?</p>
  </body>
  <!-- 延时 500ms 发送 CSS 文件 -->
  <link rel="stylesheet" href="http://localhost:8000/500.css" />
</html>

结果如下

img5.png

震惊!DOMContentLoaded 竟然在 Load 的后面(红线是 Load,蓝线是 DOMContentLoaded),至少在 Chrome 的 Performance 上给出了我们这样的答案

那么对于 HTML 的解析呢?看下图

img6.png

img7.png

HTML 解析的间隔时间出现了断层,也就是 CSS 的加载已经阻塞了 HTML 的解析,也就是 DOM 的解析,不同于在 <head> 标签的情况,DOMContentLoaded 需要等待 CSS 的完全加载,对于类似文章的观点,CSS 不会阻塞 DOM 的解析,在这个例子当中是错误的

也就是说,存在特例,<head>尾部的 CSS 会阻塞位于其后紧跟的 DOM 的解析

这种情况被称为 FOUC(Flash of Unstyled Content), 样式闪烁, 因为 First Plain 机制的存在, 会将已经解析的 DOM 和 CSSOM 结合生成渲染树并进行部分渲染, 当该 CSS 加载完成后, 才会继续解析 DOM, 而且之前已经渲染的内容将会根据解析完成的 CSS 进行重绘

注意, FOUC 在不同的浏览器下可能会有不同的表现, Firefox, Chrome, Edge 表现一致, 如果要考虑其他浏览器其自行测试

CSS 部分 - 结论

页面加载 - 以展现内容的时机为基准

  1. CSS 放在头部加载 - 会阻塞页面加载, 页面内容会等待 CSS 的加载完成, 即使它需要加载 10s
  2. CSS 放在尾部加载 - 不会阻塞页面加载, 因为 First Plain 机制, CSS 前的已经被解析页面内容的会被渲染, 但是存在重绘

DOM 解析 - 以触发 DOMContentLoaded 的时机为基准

  1. CSS 放在头部加载 - 不会阻塞 DOM 解析
  2. CSS 放在尾部加载 - 会直接阻塞 DOM 解析

此时 CSS 特指外联样式表 <link rel="stylesheet" href="xxx">

JS 和 CSS

JS 的确阻塞 DOM 的解析,详细的内容可以看这篇文章,页面访问时渲染过程中 HTML、JS 的关系,那如果是 JS 和 CSS 一起呢?上面的分析给出了外联样式 CSS 放在头部和尾部是有不同的情况的,而没有 CSS 的情况下,把 JS 放在 HTML 任意部分都会阻塞 DOM 的解析,那么 CSS 混合 JS 就可以有以下 4 种情况

  1. 头部中引入外联样式 <head>...<link />...</head>

    1. JS 在 CSS 前 <head>...<script></script>...<link />...</head>
    2. JS 在 CSS 后

      1. <head>...<link />...<script></script>...</head>
      2. <head>...<link />...</head>......<script></script>
  2. 尾部中引入外联样式 <body></body>...<link />

    1. JS 在 CSS 前 <body></body>...<link />...<script></script>
    2. JS 在 CSS 前 <body></body>...<script></script>...<link />

例子如下

<!-- 1 -->
<!DOCTYPE html>
<html>
  <head>
    <script>
      console.log(document.querySelector("p"));
    </script>
    <link rel="stylesheet" href="http://localhost:8000/big.css" />
  </head>

  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
</html>

第一种情况,JS 会阻塞 DOM 的解析,先执行 <scirpt> 的内容,无需等待 CSS 加载

<!-- 2 -->
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="http://localhost:8000/big.css" />
    <script>
      console.log(document.querySelector("p"));
    </script>
  </head>

  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
</html>

第二种情况,JS 需要等待 CSS 的加载完成才能够执行,因为 JS 可能需要获取到 CSS 中设置好的样式属性,比如宽度和高度等,那么后面的 DOM 就无法解析了,因此 CSS 加载 -> JS 等待 CSS 加载 -> JS 阻塞 DOM

img8.png

注意,补充一个小细节,看看下面这个例子

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="http://localhost:8000/big.css" />
  </head>
  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
  <script>
    console.log(document.querySelector("p"));
  </script>
</html>

同样是 CSS 在 JS 前,head 中的 JS 是获取不到 <p> 的,但 <body> 下面的是能获取到的,证明虽然 DOM 的解析被阻塞了,但是已经被解析出来的 DOM 是不影响操作的

又是一个小细节

<!-- 3 -->
<!DOCTYPE html>
<html>
  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
  <script>
    console.log(document.querySelector("p"));
  </script>
  <link rel="stylesheet" href="http://localhost:8000/500.css" />
</html>

第三种情况,同第一种情况,无需等待 CSS 的加载,但是 DOMCOntentLoaded 需要等待 CSS 的加载

<!-- 4 -->
<!DOCTYPE html>
<html>
  <body>
    <p id="test">究竟什么时候解析到我呢?</p>
  </body>
  <link rel="stylesheet" href="http://localhost:8000/500.css" />
  <script>
    console.log(document.querySelector("p"));
  </script>
</html>

第四种情况,同第二种情况,需等待 CSS 的加载,但是 DOMCOntentLoaded 需要等待 CSS 的加载

因此 DOMContentLoaded 在 MDN 上的定义在实际中并不一定准确, 或者说语义上有点误解, DOMContentLoaded 的触发会因为 JS 的位置决定是否需要等待 CSS 的加载

CSS 的加载在 JS 前则会延迟 DOMContentLoaded, 原因是 CSS 的加载将会阻塞 JS 的执行, 而 DOMContentLoaded 的触发需要等待 HTML 的内联 JS 和外联 JS 的加载执行完成, 因此 CSS 间接延迟了 DOMContentLoaded 的触发

在 Firefox, Chrome, Edge 中,如果 <link rel="stylesheet" href="xxx"> 后面跟着 <script>, 则 CSS 加载完成后, 才能触发 DOMContentLoaded

JS 和 CSS 部分结论

  1. CSS 阻塞 JS 执行
  2. CSS 间接阻塞 DOMContentLoaded, 因为 JS 需要等待 CSS 加载
  3. JS 会阻塞 DOMContentLoaded 触发时间, 但不影响其操作在其之前的 DOM
  4. 页面加载(以展现内容的时机为基准), JS 不会阻塞在其之前的 HTML 内容的渲染

总结

对于 JS ,在交互上,想要正确的操作 DOM,将 JS 放在 HTML 尾部,当解析到 JS 时,虽然没有触发 DOMCOntentLoaded,但是已经可以正确操作 DOM,减少加载时间,将 JS 异步化,对于外部 JS,采用添加 defer 属性或者 async 引入

而 CSS 在头部不会阻塞 DOM 的解析,但是跟在其后的 JS 在 CSS 加载的这段时间是不能够被执行的, 即使 JS 下载完成时间比 CSS 要快, 优先加载 CSS 避免 FOUC 的出现

相关文章
|
1天前
|
JavaScript 前端开发
页面滚动触发css3动画js插件
delighters.js是一款页面滚动触发css3动画js插件。该js插件可以在页面向下滚动时,为进入浏览器视口的元素制作各种炫酷的CSS3动画效果。
24 13
|
29天前
|
数据采集 前端开发 JavaScript
捕捉页面的关键元素:用CSS选择器与Puppeteer自动抓取
本文介绍了如何使用 Puppeteer 结合 CSS 选择器抓取动态网页中的关键元素,以亚航网站的特价机票信息为例,通过设置代理 IP、User-Agent 和 Cookie 等技术手段,有效提升爬虫策略,实现高效、稳定的爬取。
捕捉页面的关键元素:用CSS选择器与Puppeteer自动抓取
|
10天前
纸屑飘落生日蛋糕场景js+css3动画特效
纸屑飘落生日蛋糕CSS3动画特效是一款js+css3制作的全屏纸屑飘落,生日蛋糕点亮庆祝动画特效。
28 3
|
20天前
|
前端开发 测试技术 定位技术
如何利用HTML和CSS构建企业级网站的全过程。从项目概述到页面结构设计,再到HTML结构搭建与CSS样式设计,最后实现具体页面并进行优化提升,全面覆盖了网站开发的关键步骤
本文深入介绍了如何利用HTML和CSS构建企业级网站的全过程。从项目概述到页面结构设计,再到HTML结构搭建与CSS样式设计,最后实现具体页面并进行优化提升,全面覆盖了网站开发的关键步骤。通过实例展示了主页、关于我们、产品展示、新闻动态及联系我们等页面的设计与实现,强调了合理布局、美观设计及用户体验的重要性。旨在为企业打造一个既专业又具吸引力的线上平台。
44 7
|
21天前
|
JavaScript
jQuery+css3制作精美的2024圣诞节倒计时页面
jQuery+css3制作精美的2024圣诞节倒计时页面动画
20 0
jQuery+css3制作精美的2024圣诞节倒计时页面
|
28天前
|
缓存 前端开发 JavaScript
优化CSS和JavaScript加载
优化CSS和JavaScript加载
|
1月前
|
缓存 前端开发 JavaScript
优化CSS和JavaScript加载
Next.js和Nuxt.js在优化CSS和JavaScript加载方面提供了多种策略和工具。Next.js通过代码拆分、图片优化和特定的CSS/JavaScript优化措施提升性能;Nuxt.js则通过代码分割、懒加载、预渲染静态页面、Webpack配置和服务端缓存来实现优化。两者均能有效提高应用性能。
|
22天前
JS+CSS3文章内容背景黑白切换源码
JS+CSS3文章内容背景黑白切换源码是一款基于JS+CSS3制作的简单网页文章文字内容背景颜色黑白切换效果。
17 0