如何从CDN异步加载多个文件(但同步执行)



当下载多个常用的javascript/css文件(例如boostrap和jquery(时,许多像这样的主题都建议使用CDN,其中一个主要参数是可以使用CDN异步加载它们。

这是怎么回事?据我所知,头中的<script>标签是同步读取的,所以在第一个CDN文件完成之前,它不会真正查看第二个CDN文件。

<script src="//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

如何使页面异步下载脚本,但同步执行?或者这真的是默认的吗?CSS文件呢,我的

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">

在这个意义上表现得有什么不同吗?在将我自己的故障切换添加到本地代码之前(对于CDN关闭的情况(,我想正确理解加载过程,以防止同步下载陷入困境。

(注意,尽管标题几乎相同,但这并不是这个问题的重复,这个问题是关于动态加载脚本的。(

还要注意,我不能使用defer(至少以我所知道的普通方式(,因为这会阻止我在CDN关闭时添加所述故障转移,例如

<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min.js"></script>
<script>    $.fn.modal || document.write('<script src="Script/bootstrap.min.js">x3C/script>')</script>

将通过简单地添加CCD_ 3而被破坏。

与其说是异步,不如说是并行性。(它们当然是相关的,但CDN关于限制从同一来源多次下载的论点是关于并行性的。(

如何使页面异步下载脚本,但同步执行脚本?

任何一个像样的浏览器,当得到你显示的三个脚本标签时,都会并行下载它们(从同一站点限制到并行(,然后按顺序执行它们。你不需要做任何事情就能实现。浏览器在HTML中提前读取以查找要获取的资源。

使用document.write添加回退脚本可能会使浏览器的功能复杂化,甚至阻止它,但您可以使用<link rel="preload" as="script" href="...">以声明方式确保它(更多关于MDN的信息(。将其与失败CDN资源的回退脚本相结合,它可能看起来像这样:

<head>
<!-- ... -->
<link rel="preload" as="script" href="//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
<link rel="preload" as="script" href="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js">
<link rel="preload" as="script" href="//maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js">
</head>
<body>
<!-- ... -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>if (!/*loaded condition*/) document.write(/*fallback*/);</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script>if (!/*loaded condition*/) document.write(/*fallback*/);</script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script>if (!/*loaded condition*/) document.write(/*fallback*/);</script>
</body>
</html>

请注意,这不会预加载回退。你可以,但即使CDN在工作,你也会加载它们,这会浪费最终用户的带宽。故障将发生在CDN不可用的可能暂时降级的情况下,降级的用户体验可能还可以。(在安排回退时,你甚至可以向用户显示一个问题的指示符,比如Gmail的"有些事情比平时花的时间更长"指示符。(


如果你为重复URL而烦恼,并且你对小剂量的document.write很满意(就像你看起来的那样(,你可以通过以下方式避免重复URL:

<head>
<!-- ... -->
<script>
var scripts = [
{
url: "//ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js",
okay: function() { return /*check it loaded*/; }
},
{
url: "//cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js",
okay: function() { return /*check it loaded*/; }
},
{
url: "//maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js",
okay: function() { return /*check it loaded*/; }
},
];
scripts.forEach(function(script) {
document.write('<link rel="preload" as="script" href="' + script.url + '">');
});
</script>
</head>
<body>
<!-- ... -->
<script>
scripts.forEach(function(script, index) {
var fallback = script.url.substring(script.url.lastIndexOf('/') + 1);
document.write('<script src="' + script.url + '"></script>');
document.write('<script>if (!scripts[' + index + '].okay()) document.write('<script src="' + fallback + '"><\/script>');</script>');
});
</script>
</body>
</html>

(由于这都是内联脚本,您不太可能转换,我将语法保持在ES5级别,以防您不得不支持过时的环境。(

我认为您仍然可以使用defer,只需将回退代码放入事件处理程序中。。。

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script

defer

此布尔属性设置为向浏览器指示脚本是在文档解析后执行的,但是在发射CCD_ 9之前。

具有defer属性的脚本将阻止DOMContentLoaded事件,直到脚本加载并完成评估。

[…]

具有defer属性的脚本将按以下顺序执行:它们出现在文档中。

。。。因此CCD_ 11可能是一个不错的选择。

或者,您也可以将回退代码放入一个单独的.js文件中,然后它也可以加载defer,这取决于报价的底部,因此按顺序执行。

最新更新