CSS 与网络性能

  • 时间:2018-12-21 23:05 作者:IT培训黄埔 来源:IT培训黄埔 阅读:812
  • 扫一扫,手机访问
摘要:CSS 是页面渲染的关键因素之一,(当页面存在外链 CSS 时,)浏览器会等待一律的 CSS 下载及解析完成后再渲染页面。关键路径上的任何推迟都会影响首屏时间,因此我们需要尽快地将 CSS 传输到客户的设施,否则,(在页面渲染之前,)客户只能看到一个空白的屏幕。最大的问题是什么?广义而言,CSS 是


CSS 与网络性能

CSS 是页面渲染的关键因素之一,(当页面存在外链 CSS 时,)浏览器会等待一律的 CSS 下载及解析完成后再渲染页面。关键路径上的任何推迟都会影响首屏时间,因此我们需要尽快地将 CSS 传输到客户的设施,否则,(在页面渲染之前,)客户只能看到一个空白的屏幕。

最大的问题是什么?

广义而言,CSS 是(渲染)性能的关键,这是因为:

  1. 浏览器直到渲染树构建完成后才会渲染页面;
  2. 渲染树由 DOM 与 CSSOM 组合而成;
  3. DOM 是 HTML 加上(同步)阻塞的 JavaScript 操作(DOM 后的)结果;
  4. CSSOM 是 CSS 规则应用于 DOM 后的结果;
  5. 使 JavaScript 非阻塞非常简单,增加 async 或者 defer 属性就可;
  6. 相对而言,要让 CSS 变为异步加载是比较困难的;
  7. 所以记住这条经验法则:(理想情况下,)最慢样式表的下载时间决定了页面渲染的时间。

基于上述考虑,我们需要尽快构建 DOM 与 CSSOM。一般情况下,DOM 的构建是相对较快,(当请求某个页面时,)服务器响应的首个请求是 HTML 文档。但一般 CSS 是作为 HTML 的子资源而存在,因而 CSSOM 的构建通常需要更长的时间。

在这篇文章中,会讲述 CSS 为何是网络瓶颈(无论是对于它自己或者是其余资源),该如何突破它,从而缩短关键路径以减少初次渲染前的等待时间。

使用关键 CSS

假如条件允许,缩短渲染前等待时间最有效的方式就是使用 Critical CSS (关键 CSS)模式:找出初次渲染所需的样式(通常是首屏相关的样式),将它们内联到 标签中,其余样式则通过异步的方式进行加载。

尽管这十分有效,但实施起来却并不容易,比方:高度动态化的网站(译者注:如 SPA)通常难以提取首屏相关的样式、提取的过程需要自动化、需要对首屏不同元素显示或者隐藏的状态作出假设、某些边界情况难以解决以及相关工具仍未成熟等问题。假如你的项目相当庞大或者是有历史包袱,这将变得更为复杂。

根据媒体类型拆分代码

假如在项目组难以执行关键 CSS 策略,可以尝试根据媒体查询拆分 CSS 文件,这也是一种可靠的策略。执行此策略后,浏览器体现如下:

  • 以非常高的优先级下载符合当前上下文(设施、屏幕尺寸、分辨率、方向等)的 CSS 文件,阻塞关键路径;
  • 以非常低的优先级下载不符合当前上下文的 CSS 文件,不会阻塞关键路径。

浏览器基本上能将未命中媒体查询的 CSS 文件推迟下载。


假如我们把一律的 CSS 代码都放在一个文件中,请求的体现如下:


CSS 与网络性能


我们可以观察到,这个单独的 CSS 文件会以 最高 的优先级下载。

根据媒体查询拆分成若干个 CSS 文件后:


浏览器会以不同的优先级下载 CSS 文件:

CSS 与网络性能

不符合当前上下文的 CSS 文件将以 _最低_ 优先级进行下载。

浏览器依然会下载一律的 CSS 文件,但只有符合当前上下文的 CSS 文件会阻塞渲染。

避免在 CSS 文件中使用 @import

为缩短渲染等待时间而努力的下一项任务非常简单:避免在 CSS 文件中使用 @import

假如理解 @import 的原理,那应该清楚它的性能并不高,使用它会阻塞渲染更长时间。这是由于我们在关键路径上创造了更多(队列式)的网络请求:

  1. 下载 HTML;
  2. 请求并下载依赖的 CSS;下载及解析完成后,本该是构造渲染树,然而;
  3. CSS 依赖了其余的 CSS,继续请求并下载 CSS 文件;
  4. 构造渲染树。

以下是相关的案例:


all.css 的内容:

@import url(imported.css);

最终,浏览器的请求瀑布图呈现为:

CSS 与网络性能

关键路径上的 CSS 文件并没有并行下载。

通过将 @imports 请求的文件改为


可以提高网络性能:

CSS 与网络性能

关键路径上的 CSS 文件是并行下载的。

注意,有一个特殊的情况值得探讨。假如你没有包含 @import 的 CSS 文件的修改权限,为了让浏览器并行下载 CSS 文件,可以往 HTML 中补充相应的 。浏览器会并行下载相应的 CSS 文件且不会重复下载 @import 引用的文件。

在 HTML 中谨慎地使用 @import

本节的内容比较奇怪。各大浏览器的相关实现上似乎都有问题,我以前提交了相关的bugs(译者注:简单说,当页面中存在:,浏览器不会并行下载,但加上引号后:,浏览器会并行下载)。

为了透彻地了解本节的内容,首先我们需要理解浏览器的预加载扫描器:各大浏览器都实现了一个名为预加载扫描器的辅助解析器。浏览器的核心解析器主要用于构建 DOM、CSSOM、运行 JavaScript 等。HTML 文档中某些标签与状态会阻塞核心解析器,因此核心解析器的运行是断断续续的。而预加载扫描器可以跳到核心解析器尚未解析的部分,用以发现其余待引用的子资源(如 CSS、JS 文件、图片等)。

一旦发现此类子资源,预加载扫描器会开始下载它们,以便核心解析器在解析到对应内容时就能使用它们(,而不是直到那一刻才开始下载该资源)。预加载扫描器的出现,使网页的加载性能提高了19%,这是一项了不起的成就,可以极大地优化客户体验。

作为开发者,需要警惕预加载扫描器背后隐藏的问题,这在后文会进行阐述。

在 HTML 中使用 @import,在以 WebKit 与 Blink 为内核的浏览器中,可能会触发它们预加载扫描器的 bug,在 Firefox 与 IE/Edge 中,则体现低效。

Firefox 与 IE / Edge:在 HTML 中将 @import 放在 JS 和 CSS 之前

在 Firefox 与 IE/Edge 中,预加载扫描器不会并行下载

会出现这样的请求瀑布图:

CSS 与网络性能

因为预加载扫描器失效,导致资源在 Firefox 中无法并行下载(IE/Edge 中有着同样的问题)。

通过上图,可以清晰地观察到:直到 JavaScript 文件下载完成之后,@import引用的 CSS 文件才开始下载。

不单

所有浏览器都存在一个鲜为人知,但符合逻辑的现象,它会对性能造成很大的影响:

在浏览器下载完该 CSS 文件之前,不会执行下面的 JS


这是正当的。当 CSS 文件尚未下载完成时,HTML 文档中任何同步的 JavaScript 代码,均不会执行。考虑以下场景:

从下面的瀑布图可以看到,JavaScript 文件在 CSSOM 构建完成之后才开始下载,完全失去了并行下载的优势:

CSS 与网络性能

虽然预加载扫描器希望能预下载 analytics.js,但对 analytics.js 的引用并非一开始就存在于 HTML 的文档之中,它是由 后面

CSS 与网络性能

交换位置之后,子资源可以并行下载,页面的整体性能提高了两倍以上。(译者注:本节的内容只同意一半, 中的代码,的确是建议先放

根据这种组织方式,我们的页面会按最佳的方式下载与执行相关代码。下面的截图中,粉色代表 JS 的执行,但它们都比较“纤细”了,希望你能看得清楚。(第一栏的(下同))第一行是整个页面的时间轴,留意该行粉色的部分,代表 JS 正在执行。第二行是首个 JS 文件的时间轴,可以看到下载完后并立即执行。第三行是 CSS 的时间轴,因此没有任何 JS 执行。最后一行是第二个 JS 文件的时间轴,可以清晰地看到,直到 CSS 下载完成后才执行。

CSS 与网络性能

注意,你应该根据页面的实际情况测试这种代码组织方式,取决于 CSS 与 JavaScript 文件大小与 JavaScript 文件执行所需的时间,可能会出现不同的结果。记得多测试!(译者注:根据实践经验, 中的代码组织基本可以按照这种方式,即 JS 在 CSS 之前,由于 中的 JS 代码基本不依赖 CSS,唯一的反例是 JS 代码体积非常大或者执行时间很长。)

放在 中。

最后一条优化策略比较新颖,它对页面性能有很大帮助,并使页面达到逐渐渲染的效果,同时易于执行。

在 HTTP/1.1 中,我们习惯于将一律的 css 打成一个文件,如 app.css:






...


...





然而,从三方面而言,渲染性能降低了:

  1. 每个页面只用到 app.css 中的部分样式:客户会下载多余的 CSS。
  2. 难以制定缓存策略:例如,某个页面使用的日期选择器更改了背景颜色,重新生成 app.css 后,旧的 app.css 缓存将失效。
  3. 整个 app.css 在解析构建完 CSSOM 之前,页面渲染被阻塞:虽然当前页面可能只用到了 17% 的 CSS代码,但(浏览器)仍需等待其余 83% 的代码下载并解析完后,才能开始渲染。

使用 HTTP/2,可以处理第一与第二点:














...


...





根据页面的不同组件下载不同的 CSS,能有效地处理冗余问题。这减少了对关键路径造成阻塞的 CSS 文件总大小。

同时,我们可以制定更有效的缓存策略,(当代码产生变化之后,)只会影响对应文件的缓存,其余的文件保持不变。

但仍有处理的问题:下载并解析一律 CSS 文件之前,页面的渲染依然是阻塞的。页面的渲染时间依然取决于最慢的 CSS 文件下载与解析的时间。假设因为某种起因,页脚的 CSS 下载需要很长时间,(即便页头的 CSSOM 已经构建完成,)浏览器也只能等待而无法渲染页头。

然而,这现象在 Chrome (v69)中得到缓解,Firefox 与 IE/Edge 也已经进行了相关的优化。 只会阻塞后续内容,而不是整个页面的渲染。这意味着我们可以用以下方式组织代码:









...



...








这样的结果是我们能逐渐渲染页面,当前面的 CSS 可用时,页面将呈现对应的内容(,而不需等待一律 CSS 下载并解析完毕)。

I假如浏览器不支持这种特性,也不会损害页面的性能。整个页面将回退为原来的模式,只有在最慢的 CSS 下载并解析完成后,才能渲染页面。

有关这种特性的更多细节,建议阅读这篇文章。

总结

本文内容比较 繁杂,成文后超出了原本的预期,尝试总结了 CSS 加载相关的一系列的最佳实践,值得仔细体会:

  • 懒加载非关键 CSS:
  • 优先加载关键 CSS,懒加载其余 CSS;
  • 或者根据媒体类型拆分 CSS 文件。
  • 避免使用 @import:
  • 在 HTML 文档中应该避免;
  • 在 CSS 文件之中更应避免;
  • 以及警惕预加载扫描器的怪异行为。
  • 关注 CSS 与 JavaScript 的顺序:
  • 在 CSS 文件后的 JavaScript 仅在 CSSOM 构建完成后才会执行;
  • 假如你的 JavaScript 不依赖 CSS;
  • 将它放置于 CSS 之前;
  • 假如 JavaScript 依赖 CSS:
  • 将它放置于 CSS 之后。
  • 仅加载 DOM 依赖的 CSS:
  • 这将提高首次渲染的速度使让页面逐渐渲染。

注意

本文叙述的内容都遵循规范或者根据浏览器的行为推导得出,然而,你应该亲身进行测试。虽然理论上是正确的,但在实践中可能会有所不同。记得好好测试!

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】Fortigate飞塔防火墙如何开启DNS转发/DNS代理(2025-10-14 23:58)
【系统环境|】有了它,再也不用担心电脑弹窗广告和病毒啦!(2025-10-14 23:57)
【系统环境|】如何关闭恼人的电脑弹窗广告?2招搞定(2025-10-14 23:55)
【系统环境|】实用软件推荐:电脑广告弹窗多?用他,都给你屏蔽掉!(2025-10-14 23:55)
【系统环境|】Nginx篇01——基本安装配置和静态页面设置(2025-10-14 23:54)
【系统环境|】Linux端口开放,查看,删除,防火墙(2025-10-14 23:53)
【系统环境|】安全HTTP头部配置: 基于CSP与HSTS的Web安全策略(2025-10-14 23:52)
【系统环境|】老K:做私域过1000万的赛道全部都聚焦在女性身上!(2025-10-14 23:51)
【系统环境|】JavaScript跨域问题: 如何解决跨域访问和资源共享的安全策略(2025-10-14 23:51)
【系统环境|】家庭七级财务防火墙(2025-10-14 23:50)
手机二维码手机访问领取大礼包
返回顶部