【问题标题】:How to asynchronously fall back when external CDN script fails? (async/defer)外部 CDN 脚本失败时如何异步回退? (异步/延迟)
【发布时间】:2017-07-08 01:51:05
【问题描述】:

我想要异步下载/执行这些脚本以防止页面呈现阻塞:


Bootstrap 依赖于 jQuery,因此它们必须按此顺序执行。这就是我使用defer 的原因,但我的问题也适用于async

如何异步回退到我自己托管的 jQuery 或 Bootstrap 版本,或任何 CDN 托管的脚本?

我见过this related question on Stackoverflow,但这些解决方案不起作用,因为:

  1. 依赖document.write,不能用于async/defer脚本;或:
  2. 它不会立即执行回退脚本,导致无法加载 jQuery 并且其他依赖于 jQuery 的脚本会在 jQuery 回退之前加载。

我尝试了以下方法,但这也导致了我上面描述的问题 #2:

<script src="failing external CDN script" defer onerror="
    var script = document.createElement('script');
    script.async = false;
    script.src = 'my own hosted version the script';
    document.body.appendChild(script);
"></script>

【问题讨论】:

  • “我有这些脚本,我想异步下载/执行以防止页面渲染阻塞:” 为什么不把 script 标签放在页面末尾,就在结束 &lt;/body&gt; 标记之前?它们不会在那里渲染阻塞,您可以毫无困难地进行回退。
  • 我 99% 确定第一个链接是完全错误的;当一个脚本被下载和它何时被执行不需要被链接(除了后者不能在前者之前发生);浏览器会提前扫描以查找要下载的内容。我还发现第二个说法非常可疑。任何官方的谷歌文档都这么说?我很高兴从 PageInsights 获得 100% 的收益,而没有看到 defer(没有双关语)。
  • @T.J.Crowder 如果您为 Bootstrap's example 运行 PageSpeed,您会看到它抱怨在关闭 &lt;/body&gt; 标记之前有一个阻塞脚本。
  • 我不会感到惊讶。 :-) 但也请注意,它将关于渲染阻塞的 CSS 和 JS 警告混为一谈,并且通常将 CSS 放在标题中(触发警告,但详细信息会告诉您它是 CSS 还是 JS)。但无论如何,我的观点是,从根本上说 script-before-end-body-tag 不是问题。

标签: javascript html cdn html-rendering


【解决方案1】:

我有这些脚本,我想异步下载/执行以防止页面呈现阻塞

最简单的方法就是将它们放在页面的末尾,就在结束 &lt;/body&gt; 标记之前。它们不会在那里渲染阻塞,您可以轻松地进行回退:

<script src="jQuery CDN"></script>
<script>
if (typeof jQuery === "undefined") {
    // ...add your local version instead...
}
</script>

您在评论中说过,如果没有 defer,PageSpeed 会在 &lt;/body&gt; 之前抱怨 script 标签。恕我直言,它没有(请注意,code.jquery.com 的链接被故意破坏):

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="favicon.png">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-12">
            Content here, turns green on click
        </div>
    </div>
</div>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://code.jquery.com/jquery-2.2.4.mn.js"></script>
<script>
if (typeof jQuery === "undefined") {
    document.write('<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"><\/script>');
} 
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script>
$(".col-md-12").on("click", function() {
    $(this).css("color", "green");
});
</script>
</body>
</html>

这从 PageSpeed 获得 100/100(如果您正确托管它——压缩等)。

现在,如果我在底部使用 CSS link,我也会在顶部使用内联 CSS 来设置首屏内容的样式。 (但我可能不会打扰,我只需将 link 放在顶部并接受点击。我只将它放在该示例的底部,因为 PageSpeed 在其关于该主题的警告中将 JS 和 CSS 混为一谈,我想证明在&lt;/body&gt; 之前的script 不会触发该错误...)


但如果你想使用defer,你必须监听error 事件,如你的问题所示。当然,这意味着您必须推迟添加依赖于 jQuery 的脚本,直到您从原始脚本或替换脚本中获得 load,因为您不能 插入 到管道。例如:

<script src="failing external CDN script" defer onerror="
    var script = document.createElement('script');
    script.async = false;
    script.onload = addDependencies;
    script.src = 'my own hosted version the script';
    document.body.appendChild(script);
" onload="addDependencies()"></script>

...addDependencies 根据脚本为事物添加script 标签。

【讨论】:

  • 您的第二个示例类似于我在我的问题中的示例。我用这种方法遇到的问题是动态加载的后备脚本不会立即执行。这会导致对 jQuery 的依赖项抛出错误,因为它们是在 jQuery 回退本身加载之前加载的。
  • @RuudLenders:是的,您必须等待加载它们,直到您从原始延迟脚本或替换脚本中获得 load,您无法插入管道(就像您可以使用非defer/async 脚本)。所以...另一个不使用defer 的好理由。 ;-)
  • 我想我会把脚本放在&lt;/body&gt; 之前,然后。我暂时忽略 PageSpeed Insights,因为它似乎错误地指责我的脚本阻塞了渲染。
  • @RuudLenders:很奇怪,我认为我没有这样做过。无论如何,编码愉快!
猜你喜欢
  • 2012-06-04
  • 2018-09-26
  • 2016-02-02
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多