如何异步加载CSS


92

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

但是,发布后,页面失去了样式。我不太确定发生了什么,因为代码可以正常工作,但是上传后的样式无效。感谢您的帮助。谢谢


2
您是否将异步应用于样式或脚本?加载页面后加载样式一段时间,还是从不出现?
kamus

我将async属性应用于样式并将其放置在标题中。
Paulina994 2015年

2
带有样式的问题是,如果您将它们延迟加载(例如在正文中),则将触发重新渲染,并且在标准中是不允许的(但是由于浏览器非常宽容,它仍然可以工作)。如果问题是来自第三方服务器的响应时间较慢,也许您可​​以直接将其托管在服务器上?
约瑟夫·恩格尔弗罗斯特

Answers:


129

触发异步样式表下载的技巧是使用一个<link>元素并为media属性设置一个无效值(我正在使用media =“ none”,但任何值都可以)。当媒体查询的结果为false时,浏览器仍将下载样式表,但是在呈现页面之前,它不会等待内容可用。

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

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

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

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

该技术使用JavaScript,但是您可以通过将等效的阻塞<link>元素包装在一个<noscript>元素中来迎合非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/


好的..我不知道您是否可以通过提及处理程序名称来引用处理程序中的元素属性。我一直认为“事件”是唯一的例外。
Teemoh

1
那么,当页面重新呈现所有内容时,如何防止其闪烁?另外,您是否内联核心应用程序外壳样式,以便您的页面具有一些初始布局,而不是呈现Lynx引以为傲的东西?
克里斯·洛夫

2
似乎仍然为我工作。完成此操作后,我知道该文件将在PageSpeed Insights中再次发出警告。
AndyWarren '18

3
这对我有用(08-july -2018)。而且它在pagespeed洞察力方面得分很高
abilash er

1
恕我直言,jabachetta使用“预加载”的较新答案现在可能是首选的解决方案。如果有人有理由认为答案仍然可取,请添加注释以说明原因。理想情况下,链接到可确认为什么/何时仍可采用此方法的资源。[假设您使用polyfillpreload在Firefox和较旧的浏览器上支持-请参阅该答案的第一条评论中的链接]。
制造商史蒂夫(Steve)

109

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,则此解决方案只需将上面记录的答案中的两行结合起来,使其更加简洁。但这要在Firefox支持preload关键字后才能起作用。再次,如上面记录的答案中所述,预加载实际上可能没有任何好处。

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

其他注意事项:

请注意,通常,如果您要异步加载CSS,通常建议您内联关键CSS,因为CSS是有原因的渲染阻止资源。

感谢filament group的许多异步CSS解决方案。


3
无需使用loadCSS,polyfill就足够了:github.com/filamentgroup/loadCSS/blob/master/src/…–
nathan

1
最后,预加载的规范状态为[W3C候选推荐标准]。(w3.org/TR/preload/…)Firefox支持它,但默认情况下仍处于禁用状态。单击答案的“广泛支持”链接,以获取控制它的Firefox标志的“我可以使用”的描述。
制造商史蒂夫(Steve),

1
已更新答案,以提供完整浏览器支持的解决方案以及其他说明。
jabacchetta

1
@jabacchetta 2020更新非常感谢,谢谢。我一直在寻找最近编写的说明,这些说明通常对浏览器的支持更好。网络上三年来发生了许多变化!
布兰迪托

显然,这样做更好onload="this.rel='stylesheet'; this.onload = null"。显然,有必要进行设置this.onloadnull避免在某些浏览器中两次调用此方法。
Flimm

9

使用media="print"onload

丝线小组最近(2019年7月)发表了一篇文章,针对如何异步加载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。)如果需要,您可以<noscript>为未启用Javascript的罕见用户添加备用。

原来的文章值得一读,因为它进入更详细的比我在这里。csswizardry.com上的这篇文章也值得一读。


5

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

1.使用media="bogus"<link>

<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>

3
示例2加载了Javascript,但是问题是关于加载CSS。如果将更<script>改为,您是否知道示例2是否也适用于CSS <style rel=stylesheet>?(只是好奇。loadCSS如果以后需要加载CSS,我将改用(即您的示例3)。)
KajMagnus

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);

1
如果将其与loadCSS方法进行比较,该方法是否有任何缺点?看来,页面正文中var newStyle = document.createElement("link"); newStyle.rel = "stylesheet"; newStyle.href = "stylesheet.css"; document.getElementsByTagName("head")[0].appendChild(newStyle);<script>标记内的经典代码可以很好地发挥作用-即使在像MSIE8这样的旧浏览器中也是如此。
TecMan

2
@TecMan是的。如您所见,此功能可以完成window.load事件。因此,下载所有内容后便开始下载。那里没有运气。您需要尽快启动非阻塞加载。
米洛什·沙科诺维奇(MilošĐakonović),

5

异步CSS加载方法

有几种方法可以使浏览器异步加载CSS,尽管没有一种方法可以像您期望的那样简单。

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

1

如果您需要以编程方式和异步方式加载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);
}

0

如果你有一个严格的内容安全策略不允许@弗拉基米尔Salguero海湾答案,你可以使用这个(请注意脚本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属性已被忽略,因为script标签没有src异步加载的功能...还是在这里真的有用吗?您还可以详细说明用作哪个值nonce吗?
菲利普

0

请注意更新答案,因为上述所有内容现在都无法打动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>

1
如果您使用的是我提到的preload关键字方法,则可能是误报(PageSpeed错误)。github.com/filamentgroup/loadCSS/issues/53
jabacchetta

1
更新:Google已经弃用了出现问题的PageSpeed版本4,然后将其关闭-这是推荐使用此异步代码的版本。尽管我尚未在PageSpeed 5中进行测试:给出了有关它们现在如何测试的描述,以及它们对“ link”的“ preload”标签的支持,但对于Google来说,jabachetta的答案现在可能是一个更好的答案。
制造商史蒂夫(Steve),

1
@ToolmakerSteve是这个答案需要经常审核。但是此代码仍然可以使您获得100的Page Speed。
kaushik gandhi

0

我尝试使用:

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

它可以正常工作,但是它也会引起累积布局偏移,因为当我们使用rel =“ preload”时,它只是下载css,而不立即应用。

例如,当DOM加载一个包含ul,li标签的列表时,默认情况下,li标签之前有一个项目符号,然后应用CSS,我将这些项目符号删除为自定义样式以列出。因此,此处发生了累积布局偏移。

有什么解决办法吗?


0

使用rel="preload"使其独立下载,然后用onload="this.rel='stylesheet'"将其应用到样式表(as="style"有必要将它应用到样式表否则onload将无法正常工作)

<link rel="preload" as="style" type="text/css" href="mystyles.css" onload="this.rel='stylesheet'">
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.