CSS 如何阻止渲染?

How does CSS blocks rendering?

"CSS is render-blocking"这个概念我还是没看明白。我非常了解 JS 是如何阻塞解析器的。但是,前者我还是有点不清楚。

举个例子:

index.html:

<!DOCTYPE html>
<html>
<head>
  <title>Some Document</title>
  <link href='cdn1.com/styles1.css' rel="stylesheet"/>
  <link href='cdn2.com/styles2.css' rel="stylesheet"/>
</head>
<body>
   ...
   ...
</body>
</html>

style1.css(来自cdn1):

body { background: blue }

style2.css(来自cdn2):

body { background: red }

现在,我们假设来自 cdn1 的 style1.css 需要 1 秒 来加载,而来自 cdn2 的 style2 需要 500 毫秒。我想知道最终用户会在以下时间线之间看到浏览器中发生的所有事情:

  1. 在时间 T < 500ms: 是否会有 FOUC 或空白页,因为 CSS 是渲染阻塞并且渲染树不会构建,直到我们有styles1.css 文件
  2. 在时间 500ms < T < 1sec: 是否会有红色页面(因为 style2.css 已经加载)、FOUC 或仍然是空白页面第 1 点中提到的原因。

此外,结果在每个浏览器上是否一致,主要是 Edge、Chrome、Firefox 和 Safari?

你需要知道的是"CSS Object Model"。 CSS 的渲染与 JS 的渲染不同。当所有 CSS 下载完毕后,'a master Tree' 就完成了,并且决定对所有 inline CSS, internal CSS, external CSS - 将应用的最终规则是什么。

这最后的树被称为 https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model

使用Angular、React、Vue等SPA(单页应用)的好处是整个页面不会刷新,所以各种步骤如:

  1. 正在重新加载HTMLDom
  2. 正在重新加载 CSS 个文件
  3. 制作"CSS Object Model"
  4. 映射 "this CSS Model" 到 HTML-Doms

已保存,这就是为什么这些速度更快的原因。

  • 希望,这有点帮助!

以下示例使用故意延迟的资源来显示发生的情况。因为它每次使用动态资源来显示不同的 ID 并提供 cache-busting,所以我不能使用堆栈片段,但我会向您展示关键代码和 link 到一个工作示例。

在第一个示例中,我们有您描述的情况。

<!DOCTYPE html>
<html>
<head>
  <title>Render Blocking Test - A</title>
  <link href='/css/styles1.css?delay=1000&x={cachebuster}' rel="stylesheet"/>
  <link href='/css/styles2.css?delay=5000&x={cachebuster}' rel="stylesheet"/>
</head>
<body>
   <h1>Render Blocking Test - A</h1>
   <b>{cachebuster}</b>
</body>
</html>

占位符“{cachebuster}”在每次刷新时被不同的 GUID 替换。两个CSS资源的延迟分别为1秒和5秒,以更清楚地显示延迟

styles1.css 包含

body { background: blue }

styles2.css 包含

body { background: red }

看到它在这里工作:http://alohci.net/text/html/render-blocking-a.htm

在 5 秒过去之前,您会看到该页面是空白的,并且所有 css 资源都已下载。或者在刷新时,前一个页面会一直显示到 5 秒过去(GUID 在 5 秒后更改)。当它和新内容出现时,背景立即变成红色,而不是白色或蓝色。这就是渲染阻塞的意思 - 在 render-blocking 资源可用之前不会完成页面的新渲染。


为了比较,请参见第二个示例。

<!DOCTYPE html>
<html>
<head>
  <title>Render Blocking Test - B</title>
  <link href='/css/styles1.css?delay=1000&x={cachebuster}' rel="stylesheet"/>
</head>
<body>
   <h1>Render Blocking Test - B</h1>
   <b>{cachebuster}</b>
   <script src="/js/script1.js?delay=4000&x={cachebuster}"></script>
   <link href='/css/styles2.css?delay=5000&x={cachebuster}' rel="stylesheet"/>
</body>
</html>

看到它在这里工作:http://alohci.net/text/html/render-blocking-b.htm

我们在这里得到的是一个 render-blocking 资源一秒钟,然后是一些内容 - 标题和 GUID - 然后是一个需要 4 秒下载的脚本,然后是一秒钟 render-blocking资源。

在这种情况下,文本和蓝色背景会在 1 秒后出现。脚本正在加载 parser-blocks,因此第二个 css 资源尚未解析,因此无法阻止渲染。因此,可以看到蓝色背景。然后加载脚本,解析第二个 css 资源,然后 render-blocks 直到它也被加载,此时背景从蓝色变为红色。

最后,您可能会注意到红色背景的出现只需要 5 秒,而不是您想象的 9 秒。那是因为当解析器被阻塞时解析器仍然可以向前看,认识到它可能需要下载第二个 css 资源并 pro-actively 获取它,即使它在解析器之前不能使用它解锁。