【问题标题】:How to load CSS Asynchronously如何异步加载 CSS
【发布时间】:2015-09-24 10:40:09
【问题描述】:

我正在尝试消除 2 个在我的网站上阻止呈现的 CSS 文件 - 它们出现在 Google Page Speed Insights 中。我遵循了不同的方法,但都没有成功。但是,最近,我发现了一个关于 Thinking Async 的帖子,当我应用此代码时:<script async src="https://third-party.com/resource.js"></script>它确实解决了这个问题。

但是,在发布后,页面失去了样式。我不太确定发生了什么,因为代码有效,但上传后的样式不起作用。非常感谢您对此的帮助。谢谢

【问题讨论】:

  • 您是否将异步应用于样式或脚本?样式在您加载页面后加载一段时间,或者它从未出现?
  • 我将 async 属性应用于样式并将它们放在标题中。
  • 样式的问题是,如果您延迟加载它们(例如在正文中),将触发重新渲染,并且在标准中是不允许的(但由于浏览器非常宽容,它会起作用反正)。如果问题是第三方服务器的响应时间很慢,也许您可​​以简单地将它们托管在您的服务器上?

标签: html css asynchronous


【解决方案1】:

2020 年更新


简单的答案(完整的浏览器支持):

<link rel="stylesheet" href="style.css" media="print" onload="this.media='all'">

记录在案的答案(带有可选的预加载和禁用脚本的后备):

 <!-- Optional, if we want the stylesheet to get preloaded. Note that this line causes stylesheet to get downloaded, but not applied to the page. Use strategically — while preloading will push this resource up the priority list, it may cause more important resources to be pushed down the priority list. This may not be the desired effect for non-critical CSS, depending on other resources your app needs. -->
 <link rel="preload" href="style.css" as="style">

 <!-- Media type (print) doesn't match the current environment, so browser decides it's not that important and loads the stylesheet asynchronously (without delaying page rendering). On load, we change media type so that the stylesheet gets applied to screens. -->
 <link rel="stylesheet" href="style.css" media="print" onload="this.media='all'">

 <!-- Fallback that only gets inserted when JavaScript is disabled, in which case we can't load CSS asynchronously. -->
 <noscript><link rel="stylesheet" href="style.css"></noscript>

预加载和异步结合:

如果您需要预加载和异步 CSS,此解决方案只需结合上面记录的答案中的两行代码,使其更加简洁。但是这个won't work in Firefox 直到他们支持preload keyword。同样,正如上面记录的答案中详述的那样,预加载实际上可能没有好处。

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

其他注意事项:

请注意,一般来说,如果您要异步加载 CSS,通常建议您内联 critical CSS,因为 CSS 是一个渲染阻塞资源 for a reason

Credit to filament group 为他们提供了许多异步 CSS 解决方案。

此方法可能不适用于启用内容安全策略。

【讨论】:

  • 不用loadCSS,polyfill就够了:github.com/filamentgroup/loadCSS/blob/master/src/…
  • Preload 的规范状态终于是 [W3C Candidate Recommendation]。(w3.org/TR/preload/…) Firefox 支持它,但仍然默认禁用;单击答案的“广泛支持”链接以获取控制它的 Firefox 标志的“我可以使用”描述。
  • 答案已更新以提供完整浏览器支持的解决方案,并附有额外说明。
  • @jabacchetta 非常感谢 2020 年的更新,谢谢。我一直在寻找特别在浏览器支持通常更好的近期编写的说明。 3 年内网络发生了很多变化!
  • 显然,最好onload="this.rel='stylesheet'; this.onload = null"。显然,有必要将 this.onload 设置为 null 以避免在某些浏览器中被调用两次。
【解决方案2】:

触发异步样式表下载的技巧是使用&lt;link&gt; 元素并为媒体属性设置无效值(我使用的是 media="none",但任何值都可以)。当媒体查询计算结果为 false 时,浏览器仍会下载样式表,但不会等待内容可用再呈现页面。

<link rel="stylesheet" href="css.css" media="none">

样式表下载完成后,媒体属性必须设置为有效值,以便将样式规则应用于文档。 onload 事件用于将 media 属性切换为 all:

<link rel="stylesheet" href="css.css" media="none" onload="if(media!='all')media='all'">

这种加载 CSS 的方法将比标准方法更快地向访问者提供可用的内容。关键 CSS 仍然可以使用通常的阻塞方法提供服务(或者您可以内联它以获得最终性能),并且可以逐步下载非关键样式并在稍后的解析/渲染过程中应用。

此技术使用 JavaScript,但您可以通过将等效的阻塞 &lt;link&gt; 元素包装在 &lt;noscript&gt; 元素中来满足非 JavaScript 浏览器的需求:

<link rel="stylesheet" href="css.css" media="none" onload="if(media!='all')media='all'"><noscript><link rel="stylesheet" href="css.css"></noscript>

你可以在www.itcha.edu.sv看到操作

来源http://keithclark.co.uk/

【讨论】:

  • 好吧.. 我不知道你可以通过提及他们的名字来引用 on-handlers 中的元素属性。我一直认为“事件”是唯一的例外。
  • 那么在重新渲染所有内容时如何防止页面闪烁?另外,您是否内联核心应用程序外壳样式,以便您的页面具有一些初始布局,而不是呈现 Lynx 引以为豪的东西?
  • 似乎仍然对我有用。完成此操作后,我知道在 PageSpeed Insights 中会收到有关此文件的警告。
  • 这对我有用(2018 年 7 月 8 日)。它在 pagespeed 洞察力方面得分很高
  • 恕我直言,jabachetta's newer answer using "preload",现在可能是首选解决方案。如果有人有理由认为 this 答案仍然更可取,请添加评论解释原因。理想情况下,链接到确认为什么/何时这种方法仍然可能更可取的资源。 [假设您使用 polyfill 在 Firefox 和旧版浏览器上支持 preload - 请参阅该答案第一条评论中的链接]。
【解决方案3】:

使用media="print"onload

The filament group recently (July 2019) published an article 给出了他们关于如何异步加载 CSS 的最新建议。尽管他们是流行的 Javascript 库 loadCSS 的开发者,但他们实际上推荐这种不需要 Javascript 库的解决方案:

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

使用media="print" 将指示浏览器不要在屏幕上使用此样式表,而是在打印上使用。浏览器确实会下载这些打印样式表,但是是异步的,这正是我们想要的。我们还希望样式表在下载后就可以使用,为此我们设置了onload="this.media='all'; this.onload = null"。 (有些浏览器会调用onload 两次,为了解决这个问题,我们需要设置this.onload = null。)如果你愿意,你可以为少数没有启用Javascript 的用户添加&lt;noscript&gt; 后备。

original article 值得一读,因为它比我在这里更详细。 This article on csswizardry.com 也值得一读。

【讨论】:

  • 这种方法有一个警告:它不适用于不使用 unsafe-inline 的 CSP script-src。示例:Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. Note that hashes do not apply to event handlers, style attributes and javascript: navigations unless the 'unsafe-hashes' keyword is present.
【解决方案4】:

您可以尝试通过多种方式获得它:

1.在脚下使用media="bogus"&lt;link&gt;

<head>
    <!-- unimportant nonsense -->
    <link rel="stylesheet" href="style.css" media="bogus">
</head>
<body>
    <!-- other unimportant nonsense, such as content -->
    <link rel="stylesheet" href="style.css">
</body>

2.以旧方式插入DOM

<script type="text/javascript">
(function(){
  var bsa = document.createElement('script');
     bsa.type = 'text/javascript';
     bsa.async = true;
     bsa.src = 'https://s3.buysellads.com/ac/bsa.js';
  (document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(bsa);
})();
</script>

3.如果您可以尝试插件,您可以尝试 loadCSS

<script>
  // include loadCSS here...
  function loadCSS( href, before, media ){ ... }
  // load a file
  loadCSS( "path/to/mystylesheet.css" );
</script>

【讨论】:

  • 示例 2 加载 Javascript,但问题是关于加载 CSS。如果您从 &lt;script&gt; 更改为 &lt;style rel=stylesheet&gt;,您知道示例 2 是否也适用于 CSS? (只是好奇。如果以后需要加载 CSS,我将使用 loadCSS(即您的示例 3)。)
【解决方案5】:

下面的函数将创建您希望异步加载的所有样式表并将其添加到文档中。 (但是,感谢Event Listener,它只会在所有窗口的其他资源都加载后才会这样做。)

请参阅以下内容:

function loadAsyncStyleSheets() {

    var asyncStyleSheets = [
    '/stylesheets/async-stylesheet-1.css',
    '/stylesheets/async-stylesheet-2.css'
    ];

    for (var i = 0; i < asyncStyleSheets.length; i++) {
        var link = document.createElement('link');
        link.setAttribute('rel', 'stylesheet');
        link.setAttribute('href', asyncStyleSheets[i]);
        document.head.appendChild(link);
    }
}

window.addEventListener('load', loadAsyncStyleSheets, false);

【讨论】:

  • 如果我们将它与 loadCSS 方法进行比较,这种方法有什么缺点吗?看起来,页面正文中&lt;script&gt; 标记内的经典代码var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle); 工作得很好——即使在像MSIE8 这样的旧浏览器中也是如此。
  • @TecMan 是的。如您所见,此函数在window.load 事件中发挥作用。因此,当所有内容下载完毕后,下载开始。那里没有运气。您需要尽快启动非阻塞加载。
【解决方案6】:

异步 ​​CSS 加载方法

有几种方法可以让浏览器异步加载 CSS,但没有一种方法像您想象的那么简单。

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">

【讨论】:

    【解决方案7】:

    如果您需要以编程方式异步加载 CSS 链接:

    // https://www.filamentgroup.com/lab/load-css-simpler/
    function loadCSS(href, position) {
      const link = document.createElement('link');
      link.media = 'print';
      link.rel = 'stylesheet';
      link.href = href;
      link.onload = () => { link.media = 'all'; };
      position.parentNode.insertBefore(link, position);
    }
    

    【讨论】:

      【解决方案8】:

      使用rel="preload" 使其独立下载,然后使用onload="this.rel='stylesheet'" 将其应用于样式表(as="style" 必须将其应用于样式表,否则onload 将不起作用)

      <link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
      

      【讨论】:

        【解决方案9】:

        如果您有严格的内容安全策略,不允许@vladimir-salgueroanswer,您可以使用这个(请记下脚本nonce):

        <script nonce="(your nonce)" async>
        $(document).ready(function() {
            $('link[media="none"]').each(function(a, t) {
                var n = $(this).attr("data-async"),
                    i = $(this);
                void 0 !== n && !1 !== n && ("true" == n || n) && i.attr("media", "all")
            })
        });
        </script>
        

        只需将以下内容添加到您的样式表引用中:media="none" data-async="true"。这是一个例子:

        <link rel="stylesheet" href="../path/script.js" media="none" data-async="true" />
        

        jQuery 示例:

        <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css" type="text/css" media="none" data-async="true" crossorigin="anonymous" /><noscript><link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css" type="text/css" /></noscript>
        

        【讨论】:

        • 我认为 async 属性被忽略了,因为脚本标签没有 src 来异步加载......或者它在这里真的有用吗?您还可以详细说明将哪个值用作nonce
        【解决方案10】:

        请注意更新答案,因为以上所有内容现在都无法打动 google pagespeed 见解。

        根据Google,这就是你应该如何实现Css的异步加载

         < noscript id="deferred-styles" >
                < link rel="stylesheet" type="text/css" href="small.css"/ >
            < /noscript >
        
        <script>
          var loadDeferredStyles = function() {
            var addStylesNode = document.getElementById("deferred-styles");
            var replacement = document.createElement("div");
            replacement.innerHTML = addStylesNode.textContent;
            document.body.appendChild(replacement)
            addStylesNode.parentElement.removeChild(addStylesNode);
          };
          var raf = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
              window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
          if (raf) raf(function() { window.setTimeout(loadDeferredStyles, 0); });
          else window.addEventListener('load', loadDeferredStyles);
        </script>
        

        【讨论】:

        • 如果您使用我提到的 preload 关键字方法,可能会出现误报(PageSpeed 错误)。 github.com/filamentgroup/loadCSS/issues/53
        • 更新:Google 已弃用,然后关闭了出现此问题的 PageSpeed 版本 4 - 这是推荐使用此异步代码的版本。 虽然我没有在 PageSpeed 5 中进行测试:鉴于他们现在如何测试的描述,以及他们对“链接”的“预加载”标签的支持,jabachetta's answer 现在可能是一个更好的答案,因为谷歌。
        • @ToolmakerSteve 是的 这个答案需要经常审核。但是这段代码仍然可以让你的页面速度达到 100。
        【解决方案11】:

        我已经尝试使用:

        <link rel="preload stylesheet" href="mystyles.css" as="style">
        

        它工作得很好,但它也增加了累积布局偏移,因为当我们使用 rel="preload" 时,它只是下载 css ,而不是立即应用。

        当DOM加载一个包含ul、li标签的列表时,默认li标签之前有一个项目符号,然后CSS应用我将这些项目符号删除到自定义样式以进行列表。 因此,累积的布局转变正在这里发生。

        有什么解决办法吗?

        【讨论】:

        • 我也在找同样的东西,你有什么东西吗:D
        猜你喜欢
        • 2018-09-18
        • 2011-07-08
        • 2018-01-06
        • 2018-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-08
        • 1970-01-01
        相关资源
        最近更新 更多