渲染流水线视角下的CSS

  1. //theme.css
  2. div{
  3. color : coral;
  4. background-color:black
  5. }
  6. <html>
  7. <head>
  8. <link href="theme.css" rel="stylesheet">
  9. </head>
  10. <body>
  11. <div>geekbang com</div>
  12. </body>
  13. </html>

image.png

  1. 请求html数据和构建DOM中间有一段空闲时间,这个空闲时间有可能成为页面渲染的瓶颈。
  2. 当渲染进程接受html文件字节流时,会先开启一个 预解析线程, 如果遇到 js文件 css文件,那么预解析线程会提前下载这些数据。预解析线程会解析出来一个外部theme.css,并发起theme.css的下载。这里也有一个空闲时间需要注意一下,就是dom构建完毕之后, theme.css文件还未下载完成的这段时间,渲染流水线无事可做,因为下一步合成布局树,而合成布局树需要CSSOM DOM 所以需要等待CSS加载结束并解析成CSSOM.

    渲染流水线为什么需要CSSOM

    和 HTML 一样,渲染引擎也是无法直接理解 CSS 文件内容的,所以需要将其解析成渲染引擎能够理解的结构,这个结构就是 CSSOM
    CSSOM 也具有两个作用,第一个是提供给 JavaScript 操作样式表的能力,第二个是为布局树的合成提供基础的样式信息。
    CSSOM 体现在 DOM 中就是document.styleSheets
    通过样式计算和计算布局就完成了最终布局树的构建。再之后,就该进行后续的绘制操作了。 ```

//theme.css div{ color : coral; background-color:black }

geekbang com
geekbang com

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/756869/1653116593811-2a341205-b0d8-4e14-b1f7-b3d3382f008e.png#clientId=u76042bb3-9d6e-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uae859bd6&margin=%5Bobject%20Object%5D&name=image.png&originHeight=487&originWidth=1142&originalType=binary&ratio=1&rotation=0&showTitle=false&size=147951&status=done&style=none&taskId=u2df816db-3ff3-46db-81fd-03a0b4ca751&title=)不过在执行 JavaScript 脚本之前,如果页面中包含了外部 CSS 文件的引用,或者通过 style 标签内置了 CSS 内容,那么渲染引擎还需要将这些内容转换为 CSSOM,因为 JavaScript 有修改 CSSOM 的能力,**所以在执行 JavaScript 之前,还需要依赖 CSSOM。也就是说 CSS 在部分情况下也会阻塞 DOM 的生成。**

//theme.css div{ color : coral; background-color:black }

//foo.js console.log(‘time.geekbang.org’)

geekbang com
geekbang com

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/756869/1653116739017-d30200f8-edc8-4d9d-972e-043715884e1e.png#clientId=u76042bb3-9d6e-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=uae23c787&margin=%5Bobject%20Object%5D&name=image.png&originHeight=518&originWidth=1142&originalType=binary&ratio=1&rotation=0&showTitle=false&size=179351&status=done&style=none&taskId=uc3ad28e7-7369-455b-937e-f69139c5400&title=)<br />从图中可以看出来,在接收到 HTML 数据之后的**预解析过程**中,HTML 预解析器识别出来了有 CSS 文件和 JavaScript 文件需要下载,然后就同时发起这两个文件的下载请求,**需要注意的是**,这两个文件的下载过程是重叠的,所以下载时间按照最久的那个文件来算<br />后面的流水线就和前面是一样的了,**不管 CSS 文件和 JavaScript 文件谁先到达,都要先等到 CSS 文件下载完成并生成 CSSOM,然后再执行 JavaScript 脚本,最后再继续构建 DOM,构建布局树,绘制页面。**
  2. <a name="nOrUg"></a>
  3. ## 影响页面展示的因素以及优化策略
  4. 从发起 URL 请求开始,到首次显示页面的内容,在视觉上经历的三个阶段。
  5. - 第一个阶段,等请求发出去之后,到提交数据阶段,这时页面展示出来的还是之前页面的内容。关于提交数据你可以参考前面[《04 | 导航流程:从输入 URL 到页面展示,这中间发生了什么?》](https://time.geekbang.org/column/article/117637)这篇文章。
  6. - 第二个阶段,提交数据之后渲染进程会创建一个空白页面,我们通常把这段时间称为解析白屏,并等待 CSS 文件和 JavaScript 文件的加载完成,生成 CSSOM DOM,然后合成布局树,最后还要经过一系列的步骤准备首次渲染。
  7. - 第三个阶段,等首次渲染完成之后,就开始进入完整页面的生成阶段了,然后页面会一点点被绘制出来。
  8. **影响第一个阶段的因素主要是网络或者是服务器处理这块儿。**<br />现在我们重点关注第二个阶段,这个阶段的主要问题是白屏时间,如果白屏时间过久,就会影响到用户体验。为了缩短白屏时间,我们来挨个分析这个阶段的主要任务,包括了解析 HTML、下载 CSS、下载 JavaScript、生成 CSSOM、执行 JavaScript、生成布局树、绘制页面一系列操作。<br />**通常情况下的瓶颈主要体现在下载 CSS 文件、下载 JavaScript 文件和执行 JavaScript。**<br />所以要想缩短白屏时长,可以有以下策略:
  9. - 通过内联 JavaScript、内联 CSS 来移除这两种类型的文件下载,这样获取到 HTML 文件之后就可以直接开始渲染流程了。
  10. - 但并不是所有的场合都适合内联,那么还可以尽量减少文件大小,比如通过 webpack 等工具移除一些不必要的注释,并压缩 JavaScript 文件。
  11. - 还可以将一些不需要在解析 HTML 阶段使用的 JavaScript 标记上 async 或者 defer
  12. - 对于大的 CSS 文件,可以通过媒体查询属性,将其拆分为多个不同用途的 CSS 文件,这样只有在特定的场景下才会加载特定的 CSS 文件。

1: 2: 3: 4: 5: 6: 7: 8: ``` 如果script放在 还有优化的意义么 这样就不会阻塞渲染了么
依然会阻塞啊,只不过DOM会提前生成,但是渲染之前还需要等待该JS的执行完成!