ホームページ > 記事 > ウェブフロントエンド > CSS と Web パフォーマンスの詳細
この記事では、CSS とネットワーク パフォーマンスについて説明します。一定の参考値があるので、困っている友達が参考になれば幸いです。
これは非常に長い記事であり、CSS 読み込みに関する関連知識を比較的包括的に紹介しています。翻訳者のレベルが限られているため、有能な学生は次のことを行うことをお勧めします。原文を直接読んでください。また、翻訳がお役に立てば幸いです。ありがとうございます ~ 以下は本文です:
私の愛のおかげで、私は CSS マジシャンと呼ばれています。 10年以上経ちますが、私のブログでは最近CSS関連の記事が少なくなりました。次に、CSS とパフォーマンスの 2 つの主要なトピックを組み合わせた記事をお届けします。
CSS はページ レンダリングの重要な要素の 1 つです (ページに外部リンク CSS がある場合) ブラウザは、すべての CSS がダウンロードされ、解析されるまで待ってからページをレンダリングします。クリティカル パスの遅延は最初の画面までの時間に影響するため、できるだけ早く CSS をユーザーのデバイスに転送する必要があります。そうしないと、(ページがレンダリングされるまで) ユーザーには空白の画面しか表示されなくなります。
大まかに言えば、CSS は (レンダリング) パフォーマンスの鍵となります:
ブラウザはレンダー ツリーが構築されるまでページをレンダリングしないためです。
最も遅いスタイル シートのダウンロード時間によって、ページのレンダリング時間が決まります。
この記事では、CSS が (それ自体または他のリソースにとって) ネットワークのボトルネックになっている理由と、それを突破してクリティカル パスを短縮し、最初のレンダリングまでの待機時間を短縮する方法について説明します。
クリティカル CSS を使用する タグにインライン化すると、他のスタイルが非同期で読み込まれます。 これは非常に効果的ですが、実装は簡単ではありません。たとえば、非常に動的な Web サイト (訳者注: SPA など) では、通常、最初の画面に関連するスタイルを抽出するのが困難です。抽出プロセスには次のようなニーズがあります。自動化する必要があり、スクロールせずに見える範囲のさまざまな要素の表示または非表示のステータスについての仮定を立てること、処理が難しい特定の特殊なケース、およびまだ未成熟な関連ツールなどの問題が必要です。プロジェクトが非常に大きい場合、または歴史的な荷物がある場合、これはさらに複雑になります。
<link rel="stylesheet" href="all.css" />
すべての CSS コードをファイルに配置すると、リクエストは次のように動作します:
highestの優先度でダウンロードされます。 メディア クエリに基づいて複数の CSS ファイルに分割した後:
<link rel="stylesheet" href="all.css" media="all" /> <link rel="stylesheet" href="small.css" media="(min-width: 20em)" /> <link rel="stylesheet" href="medium.css" media="(min-width: 64em)" /> <link rel="stylesheet" href="large.css" media="(min-width: 90em)" /> <link rel="stylesheet" href="extra-large.css" media="(min-width: 120em)" /> <link rel="stylesheet" href="print.css" media="print" />
ブラウザは異なる優先順位で CSS ファイルをダウンロードします:
CSS ファイルで
レンダリング待機時間を短縮するための次のタスクは非常に簡単です。@import 如果了解 下载 HTML; 请求并下载依赖的 CSS; CSS 依赖了其他的 CSS,继续请求并下载 CSS 文件; 构造渲染树。 以下是相关的案例: all.css 的内容: 最终,浏览器的请求瀑布图呈现为: 关键路径上的 CSS 文件并没有并行下载。 通过将 可以提高网络性能: 关键路径上的 CSS 文件是并行下载的。 注意,有一个特殊的情况值得讨论。如果你没有包含 本节的内容比较奇怪。各大浏览器的相关实现上似乎都有问题,我以前提交了相关的bugs(译者注:简单说,当页面中存在: 为了透彻地理解本节的内容,首先我们需要了解浏览器的预加载扫描器:各大浏览器都实现了一个名为预加载扫描器的辅助解析器。浏览器的核心解析器主要用于构建 DOM、CSSOM、运行 JavaScript 等。HTML 文档中某些标签与状态会阻塞核心解析器,因而核心解析器的运行是断断续续的。而预加载扫描器可以跳到核心解析器尚未解析的部分,用以发现其他待引用的子资源(如 CSS、JS 文件、图片等)。一旦发现此类子资源,预加载扫描器会开始下载它们,以便核心解析器在解析到对应内容时就能使用它们(,而不是直到那一刻才开始下载该资源)。预加载扫描器的出现,使网页的加载性能提高了19%,这是一项了不起的成就,可以极大地优化用户体验。 作为开发者,需要警惕预加载扫描器背后隐藏的问题,这在后文会进行阐述。 在 HTML 中使用 在 Firefox 与 IE/Edge 中,预加载扫描器不会并行下载 这意味着如下的 HTML: 会出现这样的请求瀑布图: 由于预加载扫描器失效,导致资源在 Firefox 中无法并行下载(IE/Edge 中有着同样的问题)。 通过上图,可以清晰地观察到:直到 JavaScript 文件下载完成之后, 不单 与 此问题最简单的解决方案是调换 最佳解决方案是完全不使用 修改后,浏览器表现更好: 浏览器并行下载资源,IE/Edge 表现相同。 对于以 Blink 或 WebKit 为内核的浏览器而言,当 因此,无需调整代码的顺序,只需要添加引号即可解决问题。但我还是建议使用另一个 未添加引号时的代码: 瀑布图: 可以看到,缺失引号会破坏 Chrome 的预加载(Opera 与 Safari 表现也是如此。) 添加引号后的代码: 添加引号后,Chrome、Opera 和 Safari 的预加载扫描器表现恢复正常, 这绝对是 WebKit 与 Blink 内核的一个 bug,是否添加引号不应成为影响预加载扫描器的因素。 感谢 Yoav 帮我追踪这个问题。 现在这个 bug 现已在 Chromium 的待修复列表中。 在上一节中,我们了解到某些引用 CSS 文件路径 的方法,会对其他资源的下载造成负面影响。在本节中,我们将探究为何稍有不慎,CSS 将延迟其他资源的下载。该问题主要出现在动态创建的 所有浏览器都存在一个鲜为人知,但符合逻辑的现象,它会对性能造成很大的影响: 在浏览器下载完该 CSS 文件之前,不会执行下面的 JS 这是合理的。当 CSS 文件尚未下载完成时,HTML 文档中任何同步的 JavaScript 代码,均不会执行。考虑以下场景: 根据这现象,CSS 文件的下载时间会对后续 如果我们将一个 从下面的瀑布图可以看到,JavaScript 文件在 CSSOM 构建完成之后才开始下载,完全失去了并行下载的优势: 尽管预加载扫描器希望能预下载 为了更安全地加载脚本,第三方服务商经常提供这样的代码片段。然而,开发者通常不信任第三方的代码,因而会把该片段放在页面的最后,但这可能会导致不良的后果。事实上,Google Analytics (在文档中)对此的建议是: 将代码复制后,作为第一项粘贴到待追踪页面的 中。 综上,我的建议是: 如果 调整一下代码: 交换位置之后,子资源可以并行下载,页面的整体性能提高了两倍以上。(译者注:本节的内容只同意一半, 这条建议远比你想象中的有用。 上文讨论了插入新 假设: 那如果 JS 并不依赖 CSSOM,以下那种情况会更快? 答案是: 如果 JS 文件没有依赖 CSS,你应该将 JS 代码放在样式表之前。 既然没有依赖,那就没有任何理由阻塞 JavaScript 代码的执行。 (尽管执行 JavaScript 代码时会停止解析 DOM, 但预加载扫描器会提前下载之后的 CSS) 如果你一部分 JavaScript 需要依赖 CSS 而另一部分却不用,最佳的实践是将 JavaScript 分为两部分,分别置于 CSS 的两侧: 根据这种组织方式,我们的页面会按最佳的方式下载与执行相关代码。下面的截图中,粉色代表 JS 的执行,但它们都比较“纤细”了,希望你能看得清楚。(第一栏的(下同))第一行是整个页面的时间轴,留意该行粉色的部分,代表 JS 正在执行。第二行是首个 JS 文件的时间轴,可以看到下载完后并立即执行。第三行是 CSS 的时间轴,因而没有任何 JS 执行。最后一行是第二个 JS 文件的时间轴,可以清晰地看到,直到 CSS 下载完成后才执行。 注意,你应该根据页面的实际情况测试这种代码组织方式,取决于 CSS 与 JavaScript 文件大小与 JavaScript 文件执行所需的时间,可能会出现不同的结果。记得多测试!(译者注:根据实践经验, 最后一条优化策略比较新颖,它对页面性能有很大帮助,并使页面达到逐步渲染的效果,同时易于执行。 在 HTTP/1.1 中,我们习惯于将全部的 css 打成一个文件,如 app.css: 然而,从三方面而言,渲染性能降低了: 每个页面只用到 app.css 中的部分样式: 用户会下载多余的 CSS。 难以制定缓存策略: 例如,某个页面使用的日期选择器更改了背景颜色,重新生成 app.css 后,旧的 app.css 缓存将失效。 整个 app.css 在解析构建完 CSSOM 之前,页面渲染被阻塞: 尽管当前页面可能只用到了 17% 的 CSS代码,但(浏览器)仍需等待其他 83% 的代码下载并解析完后,才能开始渲染。 使用 HTTP/2,可以解决第一与第二点: 根据页面的不同组件下载不同的 CSS,能有效地解决冗余问题。这减少了对关键路径造成阻塞的 CSS 文件总大小。 同时,我们可以制定更有效的缓存策略,(当代码产生变化之后,)只会影响对应文件的缓存,其他的文件保持不变。 但仍有解决的问题:下载并解析全部 CSS 文件之前,页面的渲染仍然是阻塞的。页面的渲染时间仍然取决于最慢的 CSS 文件下载与解析的时间。假设由于某种原因,页脚的 CSS 下载需要很长时间,(即使页头的 CSSOM 已经构建完成,)浏览器也只能等待而无法渲染页头。 然而,这现象在 Chrome (v69)中得到缓解,Firefox 与 IE/Edge 也已经进行了相关的优化。 这样的结果是我们能逐步渲染页面,当前面的 CSS 可用时,页面将呈现对应的内容(,而不需等待全部 CSS 下载并解析完毕)。 I如果浏览器不支持这种特性,也不会损害页面的性能。整个页面将回退为原来的模式,只有在最慢的 CSS 下载并解析完成后,才能渲染页面。 有关这种特性的更多细节,建议阅读这篇文章。 本文内容比较 繁杂,成文后超出了本来的预期,尝试总结了 CSS 加载相关的一系列的最佳实践,值得仔细体会: 本文叙述的内容都遵循规范或根据浏览器的行为推导得出,然而,你应该亲自进行测试。尽管理论上是正确的,但在实践中可能会有所不同。记得好好测试! 英文原文地址:https://csswizardry.com/2018/11/css-and-network-performance/ 更多编程相关知识,请访问:编程视频!!@import
的原理,那应该清楚它的性能并不高,使用它会阻塞渲染更长时间。这是因为我们在关键路径上创造了更多(队列式)的网络请求:
<link rel="stylesheet" href="all.css" />
@import url(imported.css);
@imports
请求的文件改为 4433a343954f80db8c2f22a0cefc31bd
:<link rel="stylesheet" href="all.css" />
@import
的 CSS 文件的修改权限,为了让浏览器并行下载 CSS 文件,可以往 HTML 中补充相应的 4988ca3665f65e963a13d338db32ad78
。浏览器会并行下载相应的 CSS 文件且不会重复下载 @import
引用的文件。在 HTML 中谨慎地使用
@import
c9ccee2e6ea535a969eb3f532ad9fe89@import url(xxx.url);531ac245ce3e4fe3d50054a55f265927
,浏览器不会并行下载,但加上引号后:c9ccee2e6ea535a969eb3f532ad9fe89@import url("xxx.url");531ac245ce3e4fe3d50054a55f265927
,浏览器会并行下载)。@import
,在以 WebKit 与 Blink 为内核的浏览器中,可能会触发它们预加载扫描器的 bug,在 Firefox 与 IE/Edge 中,则表现低效。Firefox 与 IE / Edge:在 HTML 中将
@import
放在 JS 和 CSS 之前642828b032459905d775329ad60a089b
和 4433a343954f80db8c2f22a0cefc31bd
后 @imports
引用的资源。<script src="app.js"></script>
<style>
@import url(app.css);
</style>
@import
引用的 CSS 文件才开始下载。3f1c4e4b6b16bbbd69b2ee476dc4f83a
标签会触发此问题,2cdf5bf648cf2f33323966d7f58a7f3f
标签也会:<link rel="stylesheet" href="style.css" />
<style>
@import url(app.css);
</style>
3f1c4e4b6b16bbbd69b2ee476dc4f83a
标签一样,子资源无法并行下载。3f1c4e4b6b16bbbd69b2ee476dc4f83a
或 4433a343954f80db8c2f22a0cefc31bd
标签与(包含 @import
的)c9ccee2e6ea535a969eb3f532ad9fe89
标签的位置。然而,当我们改变顺序时,可能会对页面造成影响。@import
,再往 HTML 文档中加入另一个 4433a343954f80db8c2f22a0cefc31bd
取而代之:<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="app.css" />
以 Blink 或 WebKit 内核的浏览器:在 HTML 文档中使用
@import
时,要用引号包裹 url。@import
引用的 url 未被引号包裹时,表现与 Firefox 和 IE/Edge 一致(无法并行下载)。这意味着上述两个内核的预加载扫描器存在 bug。4433a343954f80db8c2f22a0cefc31bd
取代 @import
。<link rel="stylesheet" href="style.css" />
<style>
@import url(app.css);
</style>
<link rel="stylesheet" href="style.css" />
<style>
@import url("app.css");
</style>
不要将动态插入 JavaScript 的代码放在
4433a343954f80db8c2f22a0cefc31bd
之后3f1c4e4b6b16bbbd69b2ee476dc4f83a
标签中:<script>
var script = document.createElement('script');
script.src = "analytics.js";
document.getElementsByTagName('head')[0].appendChild(script);
</script>
<link rel="stylesheet" href="slow-loading-stylesheet.css" />
<script>
console.log("I will not run until slow-loading-stylesheet.css is downloaded.");
</script>
3f1c4e4b6b16bbbd69b2ee476dc4f83a
中的代码会访问当前的页面样式,为确保结果正确,需要等待( 3f1c4e4b6b16bbbd69b2ee476dc4f83a
标签前)所有 CSS 文件下载并解析完毕后再获取,否则无法保证正确性。因此,在 CSSOM 构建完成之前,3f1c4e4b6b16bbbd69b2ee476dc4f83a
中的代码不会执行。3f1c4e4b6b16bbbd69b2ee476dc4f83a
的执行时间造成影响。下面的例子能较好地说明问题。4433a343954f80db8c2f22a0cefc31bd
放在 3f1c4e4b6b16bbbd69b2ee476dc4f83a
之前,3f1c4e4b6b16bbbd69b2ee476dc4f83a
中动态创建新 3f1c4e4b6b16bbbd69b2ee476dc4f83a
的代码只会在 CSS 文件下载完之后才会执行,这意味着 CSS 推迟了资源的下载与执行:
<script>
var script = document.createElement('script');
script.src = "analytics.js";
document.getElementsByTagName('head')[0].appendChild(script);
</script>
analytics.js
,但对 analytics.js
的引用并非一开始就存在于 HTML 的文档之中,它是由 2cdf5bf648cf2f33323966d7f58a7f3f
后面 3f1c4e4b6b16bbbd69b2ee476dc4f83a
的代码动态创建的,在创建之前,它只是一些字符串,而不是预加载扫描器可识别的资源,无形中它被隐藏起来了。3f1c4e4b6b16bbbd69b2ee476dc4f83a
中的代码并不依赖 CSS,把它们放在样式表之前。<script>
var script = document.createElement('script');
script.src = "analytics.js";
document.getElementsByTagName('head')[0].appendChild(script);
</script>
93f0f5c25f18dab9d176bd4f6de5d30e
中的代码,确实是建议先放 3f1c4e4b6b16bbbd69b2ee476dc4f83a
,再放 2cdf5bf648cf2f33323966d7f58a7f3f
,后文也会有相关的内容,但第三方代码放在 93f0f5c25f18dab9d176bd4f6de5d30e
中的第一项,取决于相关代码的用途。如非必要,放在页面末尾或空闲时下载及执行也未尝不可)将无需查询 CSSOM 的 JavaScript 代码放在 CSS 文件之前,需要查询的放在 CSS 文件之后
3f1c4e4b6b16bbbd69b2ee476dc4f83a
的代码应放在 2cdf5bf648cf2f33323966d7f58a7f3f
之前,那是否能推广到其他的 CSS 与 JavaScript 呢?为了弄明白这个问题,先提出以下假设:
<!-- 这部分 JavaScript 代码下载完后会立即执行 -->
<script src="i-need-to-block-dom-but-DONT-need-to-query-cssom.js"></script>
<link rel="stylesheet" href="app.css" />
<!-- 这部分 JavaScript 代码在 CSSOM 构建完成后才会执行 -->
<script src="i-need-to-block-dom-but-DO-need-to-query-cssom.js"></script>
93f0f5c25f18dab9d176bd4f6de5d30e
中的代码组织基本可以按照这种方式,即 JS 在 CSS 之前,因为 93f0f5c25f18dab9d176bd4f6de5d30e
中的 JS 代码基本不依赖 CSS,唯一的反例是 JS 代码体积非常大或执行时间很长。)将
4433a343954f80db8c2f22a0cefc31bd
放在 6c04bd5ca3fcae76e30b72ad730ca86d
中。<html>
<head>
<link rel="stylesheet" href="app.css" />
</head>
<body>
<header class="site-header">
<nav class="site-nav">...</nav>
</header>
<main class="content">
<section class="content-primary">
<h1>...</h1>
<div class="date-picker">...</div>
</section>
<aside class="content-secondary">
<div class="ads">...</div>
</aside>
</main>
<footer class="site-footer">
</footer>
</body>
<html>
<head>
<link rel="stylesheet" href="core.css" />
<link rel="stylesheet" href="site-header.css" />
<link rel="stylesheet" href="site-nav.css" />
<link rel="stylesheet" href="content.css" />
<link rel="stylesheet" href="content-primary.css" />
<link rel="stylesheet" href="date-picker.css" />
<link rel="stylesheet" href="content-secondary.css" />
<link rel="stylesheet" href="ads.css" />
<link rel="stylesheet" href="site-footer.css" />
</head>
<body>
<header class="site-header">
<nav class="site-nav">...</nav>
</header>
<main class="content">
<section class="content-primary">
<h1>...</h1>
<div class="date-picker">...</div>
</section>
<aside class="content-secondary">
<div class="ads">...</div>
</aside>
</main>
<footer class="site-footer">
</footer>
</body>
4433a343954f80db8c2f22a0cefc31bd
只会阻塞后续内容,而不是整个页面的渲染。这意味着我们可以用以下方式组织代码:<html>
<head>
<link rel="stylesheet" href="core.css" />
</head>
<body>
<link rel="stylesheet" href="site-header.css" />
<header class="site-header">
<link rel="stylesheet" href="site-nav.css" />
<nav class="site-nav">...</nav>
</header>
<link rel="stylesheet" href="content.css" />
<main class="content">
<link rel="stylesheet" href="content-primary.css" />
<section class="content-primary">
<h1>...</h1>
<link rel="stylesheet" href="date-picker.css" />
<div class="date-picker">...</div>
</section>
<link rel="stylesheet" href="content-secondary.css" />
<aside class="content-secondary">
<link rel="stylesheet" href="ads.css" />
<div class="ads">...</div>
</aside>
</main>
<link rel="stylesheet" href="site-footer.css" />
<footer class="site-footer">
</footer>
</body>
总结
@import
:
注意
以上がCSS と Web パフォーマンスの詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。