css阻塞_当CSS阻塞时

css阻塞

css阻塞

The other day I was auditing a site and ran into a pattern that I’ve seen with a few different clients now. The pattern itself is no longer recommended, but it’s a helpful illustration of why it’s important to be careful about how you use preload as well as a useful, real-world demonstration of how the order of your document can have a significant impact on performance (something Harry Roberts has done an outstanding job of detailing).

前几天,我正在审核一个站点,并遇到了一种我现在与几个不同客户一起看到的模式。 不再建议使用该模式本身,但是它是一个有用的说明,说明了为什么要谨慎使用preload很重要,以及在现实世界中演示文档顺序如何对性能产生重大影响的有用演示(哈里·罗伯茨(Harry Roberts) 在细节方面做得非常出色 )。

I’m a big fan of the Filament Group—they churn out an absurd amount of high-quality work, and they are constantly creating invaluable resources and giving them away for the betterment of the web. One of those great resources is their loadCSS project, which for the longest time, was the way I recommended folks load their non-critical CSS.

我是Filament Group的忠实拥护者-他们制作了大量荒谬的高质量作品,并且他们不断创造宝贵的资源,并为改善网络提供了资源。 其中那些伟大的资源是他们的loadCSS项目 ,其中时间最长的,是我推荐的乡亲加载其非关键CSS 方式。

While that’s changed (and Filament Group wrote up a great post about what they prefer to do nowadays), I still find it often used in production on sites I audit.

虽然情况发生了变化(Filament Group撰写了一篇有关他们如今喜欢做什么的好文章),但我仍然发现它经常用在我审核过的站点上。

One particular pattern I’ve seen is the preload/polyfill pattern. With this approach, you load any stylesheets as preloads instead, and then use their onload events to change them back to a stylesheet once the browser has them ready. It looks something like this:

我见过的一种特殊模式是预加载/填充模式。 使用这种方法,您可以将所有样式表加载为预加载,然后在浏览器准备就绪后使用它们的onload事件将其更改回样式表。 看起来像这样:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="path/to/mystylesheet.css"></noscript>

Since not every browser supports preload, the loadCSS project provides a helpful polyfill for you to add after you’ve declared your links, like so:

由于并非所有浏览器都支持preload ,所以loadCSS项目提供了一个有用的polyfill,供您在声明链接后添加,例如:

<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

网络优先 (Network Priorities Out of Whack)

I’ve never been super excited about this pattern. Preload is a bit of a blunt instrument—whatever you apply to it is gonna jump way up in line to be downloaded. The use of preload means that these stylesheets, which you’re presumably making asynchronous because they aren’t very critical to page display, are given a very high priority by browsers.

我从未对这种模式感到非常兴奋。 预加载是一个比较钝的工具-无论您应用什么,都可以直接下载。 使用preload意味着这些样式表(由于它们对页面显示不是很关键,所以您认为它们是异步的)被浏览器赋予了很高的优先级。

The following image from a WebPageTest run shows the issue pretty well. Lines 3-6 are CSS files that are being loaded asynchronously using the preload pattern. But, while developers have flagged them as not important enough to block rendering, the use of preload means they are arriving before the remaining resources.

下图来自WebPageTest运行,很好地显示了该问题。 第3-6行是使用预加载模式异步加载CSS文件。 但是,尽管开发人员已将它们标记为不够重要,以至于无法阻止渲染,但使用preload意味着它们将在剩余资源之前到达。

Lines 3-6 are CSS files being loaded asynchronously using the preload pattern. While they aren&rsquo;t critical to initial render, the use of preload means they arrive before anything else in this case.

Lines 3-6 are CSS files being loaded asynchronously using the preload pattern. While they aren’t critical to initial render, the use of preload means they arrive before anything else in this case.

第3-6行是使用预加载模式异步加载CSS文件。 尽管它们对初始渲染并不重要,但是使用预加载意味着在这种情况下它们比其他任何对象先到达。

阻止HTML解析器 (Blocking the HTML parser)

The network priority issues are enough of a reason to avoid this pattern in most situations. But in this case, the issues were compounded by the presence of another stylesheet being loaded externally.

在大多数情况下,网络优先级问题足以避免此模式。 但是在这种情况下,由于另一个样式表从外部加载而使问题更加复杂。

<link rel="stylesheet" href="path/to/main.css" />
<link rel="preload" href="path/to/mystylesheet.css" as="style" onload="this.rel='stylesheet'">
<noscript>
    <link rel="stylesheet" href="path/to/mystylesheet.css">
</noscript>
<script>
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */
(function(){ ... }());
</script>

You still have the same issues with the preload making these non-critical stylesheets have a high priority, but just as critically and perhaps a bit less obvious is the impact this has on the browsers ability to parse the page.

preload仍然存在相同的问题,使得这些非关键样式表具有较高的优先级,但同样重要的是,这可能对浏览器解析页面的能力产生的影响也不太明显。

Again, Harry’s already wrote about what happens here in great detail, so I recommend reading through that to better understand what’s happening. But here’s the short version.

再次, Harry已经详细描述了这里发生的事情 ,因此,我建议通读该书,以更好地了解正在发生的事情。 但是这是简短的版本。

Typically, a stylesheet blocks the page from rendering. The browser has to request and parse it to be able to display the page. It does not, however, stop the browser from parsing the rest of the HTML.

通常,样式表会阻止页面呈现。 浏览器必须请求并解析它才能显示该页面。 但是,它不会阻止浏览器解析HTML的其余部分。

Scripts, on the other hand, do block the parser unless they are marked as defer or async.

另一方面,除非标记为deferasync否则脚本阻止解析器。

Since the browser has to assume that a script could potentially manipulate either the page itself or the styles that apply to the page, it has to be careful about when that script executes. If it knows that it’s still requesting some CSS, it will wait until that CSS has arrived before the script itself gets run. And, since it can’t continue parsing the document until the script has run, that means that stylesheet is no longer just blocking rendering—it’s preventing the browser from parsing the HTML.

由于浏览器必须假设脚本可以潜在地操纵页面本身或应用于页面的样式,因此必须谨慎执行该脚本。 如果知道仍在请求CSS,它将等待CSS到达后再运行脚本本身。 而且,由于它无法在脚本运行之前继续解析文档,因此这意味着样式表不再只是阻止呈现-它阻止了浏览器解析HTML。

This blocking behavior is true for external scripts, but also inline script elements. If CSS is still being downloaded, inline scripts won’t run until that CSS arrives.

这种阻止行为对于外部脚本和内联脚本元素均适用。 如果仍在下载CSS,则内联脚本要等到CSS到达后才能运行。

看到问题 (Seeing the problem)

The clearest way I’ve found to visualize this is to look at Chrome’s developer tools (gosh, I love how great our tools have gotten).

我发现最清晰的可视化方法是查看Chrome的开发人员工具(老天,我喜欢我们的工具取得的成就)。

In Chrome, you can use the Performance panel to capture a profile of the page load. (I recommend using a throttled network setting to help make the issue even more apparent.)

在Chrome中,您可以使用“效果”面板来捕获页面加载的配置文件。 (我建议使用限制的网络设置,以使问题更加明显。)

For this test, I ran a test using a Fast 3G setting. Zooming in on the main thread activity, you can see that the request for the CSS file occurs during the first chunk of HTML parsing (around 1.7s into the page load process).

对于此测试,我使用Fast 3G设置进行了测试。 放大主线程活动,您可以看到对CSS文件的请求发生在HTML解析的第一块期间(进入页面加载过程大约1.7s)。

That tiny sliver of activity directly below the Parse HTML block is when the CSS is first requested, 1.7 seconds into the page load process.

That tiny sliver of activity directly below the Parse HTML block is when the CSS is first requested, 1.7 seconds into the page load process.

在首次请求CSS时(即页面加载过程的1.7秒),Parse HTML块正下方的一小部分活动。

For the next second or so, the main thread goes quiet. There are some tiny bits of activity—load events firing on the preloaded stylesheets, more requests being sent by the browser’s preloader—but the browser has stopped parsing the HTML entirely.

在下一秒左右的时间内,主线程变得安静。 有一些活动:加载事件在预加载的样式表上触发,更多的请求由浏览器的预加载器发送,但浏览器已停止完全解析HTML。

When you zoom back out in Chrome&rsquo;s Performance panel, you can see the main thread goes quiet after the CSS is requested for over 1.1 seconds.

When you zoom back out in Chrome’s Performance panel, you can see the main thread goes quiet after the CSS is requested for over 1.1 seconds.

当您在Chrome的“性能”面板中缩小时,可以看到在请求CSS超过1.1秒后主线程变得安静。

Around 2.8s, the stylesheet arrives, and the browser parses it. Only then do we see the inline script get evaluated, followed by the browser finally moving on with parsing the HTML.

大约2.8秒,样式表到达,浏览器对其进行解析。 只有这样,我们才能看到内联脚本得到评估,然后浏览器终于继续解析HTML。

Finally, the CSS arrives around 2.8 seconds into the load process, and we see the browser starts parsing the HTML again.

Finally, the CSS arrives around 2.8 seconds into the load process, and we see the browser starts parsing the HTML again.

最终,CSS到达加载过程大约2.8秒,我们看到浏览器再次开始解析HTML。

Firefox例外 (The Firefox Exception)

This blocking behavior is true of Chrome, Edge, and Safari. The one exception of note is Firefox.

对于Chrome,Edge和Safari,这种阻止行为是正确的。 值得注意的一个例外是Firefox。

Every other browser pauses HTML parsing but uses a lookahead parser (preloader) to scan for external resources and make requests for them. Firefox, however, takes it one step further: they’ll speculatively build the DOM tree even though they’re waiting on script execution.

其他所有浏览器均会暂停HTML解析,但会使用前瞻解析器(预加载器)扫描外部资源并对其进行请求。 但是,Firefox向前迈进了一步:即使他们在等待脚本执行,他们也会以推测的方式构建DOM树

As long as the script doesn’t manipulate the DOM and cause them to throw that speculative parsing work out, it lets Firefox get a head start. Of course, if they do have to throw it out, then that speculative work accomplishes nothing.

只要脚本不操纵DOM并导致他们放弃推测性解析工作,它就可以让Firefox抢先一步。 当然,如果他们不得不把它扔出去,然后是投机性的工作一事无成。

It’s an interesting approach, and I’m super curious about how effective it is. Right now, however, there’s no visibility into this in Firefox’s performance profiler. You can’t see this parsing work in their profiler, whether that work had to be redone and, if so, what the performance cost was.

这是一个有趣的方法,和我超级好奇它是多么有效。 但是,目前在Firefox的性能分析器中对此没有可见性。 您无法在他们的探查器中看到此解析工作,是否必须重做该工作以及(如果需要)性能成本。

I chatted with the fine folks working on their developer tools, though, and they had some exciting ideas for how they might be able to surface that information in the future—fingers crossed!

但是,我与正在使用他们的开发人员工具的优秀人员聊天,他们对于如何在将来展示这些信息有一些令人兴奋的想法-手指交叉!

解决问题 (Fixing the issue)

In this client’s case, the first step to fixing this issue was pretty straightforward: ditch the preload/polyfill pattern. Preloading non-critical CSS kind of defeats the purpose and switching to using a print stylesheet instead of a preload, as Filament Group themselves now recommend, allows us to remove the polyfill entirely.

在此客户的情况下,解决此问题的第一步非常简单:放弃预加载/填充模式。 如Filament Group自己现在所建议的那样, preload非关键CSS会破坏目的,并切换到使用print样式表而不是preload ,这使我们可以完全删除polyfill。

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

That already puts us in a better state: the network priorities now line up much better with the actual importance of the assets being downloaded, and we’ve eliminated that inline script block.

这已经使我们处于更好的状态:网络优先级现在与要下载的资产的实际重要性更加一致,并且我们消除了该内联脚本块。

In this case, there was still one more inline script in the head of the document after the CSS was requested. Moving that script ahead of the stylesheet in the DOM eliminated the parser blocking behavior. Looking at the Chrome Performance panel again, the difference is clear.

在这种情况下,在请求CSS之后,文档的头中还有一个内联脚本。 将该脚本移到DOM中的样式表之前可以消除解析器阻止行为。 再次查看“ Chrome性能”面板,区别很明显。

Before the changes, the browser stopped parsing at line 1939 of the HTML, when it encountered the inline script and stayed there for over a second. After the change, it was able to parse through line 5281.

Before the changes, the browser stopped parsing at line 1939 of the HTML, when it encountered the inline script and stayed there for over a second. After the change, it was able to parse through line 5281.

在进行更改之前,当浏览器遇到内联脚本并停留在那里一秒钟以上时,它停止在HTML的1939行进行解析。 更改后,它能够通过5281行进行解析。

Whereas before it was stopped at line 1939 waiting for the CSS to load, it now parses through line 5281, where another inline script occurs at the end of the page, once again stopping the parser.

尽管在1939行停止等待CSS加载之前,它现在通过5281行进行了解析,在该行的最后,另一个内联脚本出现在页面末尾,再次停止了解析器。

This is a quick fix, but it’s also not the one that will be the final solution. Switching the order and ditching the preload/polyfill pattern is just the first step. Our biggest gain here will come from inlining the critical CSS instead of referencing it in an external file (the preload/polyfill pattern is intended to be used alongside inline CSS). That lets us ignore the script related issues altogether and ensures that the browser has all the CSS it needs to render the page in that first network request.

这是一个快速解决方案,但不是最终解决方案。 切换顺序和放弃预加载/填充模式只是第一步。 我们最大的收获将来自于内嵌关键CSS,而不是在外部文件中引用它(预加载/填充模式旨在与内联CSS一起使用)。 这样我们就可以完全忽略与脚本相关的问题,并确保浏览器拥有在第一个网络请求中呈现页面所需的所有CSS。

For now, though, we can get a nice performance boost through a minor change to the way we load CSS and the DOM order.

不过,就目前而言,我们可以通过对加载CSS和DOM顺序的方式进行较小的更改来获得不错的性能提升。

Long story short:

长话短说:

  • If you’re using loadCSS with the preload/polyfill pattern, switch to the print stylesheet pattern instead.

    如果您将loadCSS与preload / polyfill模式一起使用,请改为使用print样式表模式。

  • If you have any external stylesheets that you’re loading normally (that is, as a regular stylesheet link), move any and all inline scripts that you can above it in the markup.

    如果您有任何正常加载的外部样式表(即,作为常规样式表链接),则将可以在标记上方移动的所有内联脚本移动到标记之上。
  • Inline your critical CSS for the fastest possible start render times.

    内联关键CSS,以最快的速度开始渲染。

翻译自: https://timkadlec.com/remembers/2020-02-13-when-css-blocks/

css阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值