如何推迟内联 Javascript?

IT技术 javascript jquery html deferred-execution
2021-03-16 18:17:59

我有以下 html 代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/blazy/1.8.2/blazy.min.js" defer></script>
    <script src="https://code.jquery.com/jquery-2.1.4.min.js" integrity="sha256-8WqyJLuWKRBVhxXIL1jBDD7SDxU936oZkCnxQbWwJVw=" crossorigin="anonymous" defer></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.9.0/js/lightbox.min.js" defer></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous" defer></script>
    <!-- 26 dec flexslider js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/flexslider/2.6.3/jquery.flexslider.min.js" defer></script>
    <script defer>
    (function($) {
        $(document).ready(function() {
            //do something with b-lazy plugin, lightbox plugin and then with flexslider
        });
    })(jQuery);
    </script>
</head>
<body>
</body>
</html>

我收到一个错误,说 jQuery 未定义。现在,即使我从内联 JS 代码中删除了 defer,它也会说 jQuery 未定义。出于某种原因,我必须保留 jQuery 插件并保持我的 JS 代码内联。我的问题是:

  1. 为什么内联 Javascript 代码defer在其上存在属性时不会延迟

  2. 有没有办法模仿我的内联 Javascript 代码上的延迟行为?如果需要,我可以把它放在 body 标签的末尾。

6个回答

具有defer属性的脚本按指定的顺序加载,但不是在加载文档本身之前由于deferscript标签没有影响,除非它们也具有该src属性,因此第一个执行的脚本是您的内联脚本。所以当时 jQuery 还没有加载。

您至少可以通过两种方式解决此问题:

  • 将您的内联脚本放在一个.js文件中,并使用一个src属性(除了defer您已经拥有属性之外)引用它,或者

  • 让您的内联脚本等待文档和延迟脚本被加载。DOMContentLoaded事件将在发生时触发:

    <script>
        window.addEventListener('DOMContentLoaded', function() {
            (function($) {
                //do something with b-lazy plugin, lightbox plugin and then with flexslider
            })(jQuery);
        });
    </script>
    

注意:请注意,在后一种情况下$(document).ready(function()不再包含在内,因为它会等待相同的事件 ( DOMContentLoaded)。可能还包括像您在最初的代码有,但随后的jQuery只会执行回调马上,这使得没有实际的区别。

谢谢你的回答。是否确定DOMContentLoaded在加载所有延迟脚本后会触发事件?
2021-04-26 18:17:59
是的,这是肯定的。
2021-05-01 18:17:59
在某些情况下,如果您这样做,代码将不再起作用。例如,如果代码有document.write,那么它的行为就会完全不同。
2021-05-02 18:17:59
我必须说我从来没有遇到过 JS 在 DOM 准备好但 CSSOM 没有准备好的情况下运行的问题。也许(!)如果 JS 访问 CSS 属性,这可能会阻止 JS 执行,直到 CSSOM 准备好,但我真的不知道。这可能是一个新问题的主题(但请检查之前是否有人问过它)。
2021-05-12 18:17:59
虽然这是一个很好的解决方案,但它仍然是渲染阻塞,这可能首先违背了推迟 jQuery 的目的。对于真正的非阻塞执行,您最好使用此技巧模拟内联 javascript 块上的“延迟”属性
2021-05-20 18:17:59

您可以从脚本中创建一个 Base64 URL 并将其放入 src 中!

<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
        defer>
</script>

我建立了一个快速测试来查看它的运行情况。Hello world!如果defer工作正常,您应该看到带有last的警报

<script defer>
  alert('Why no defer?!?');
</script>

<!-- alert('Hello world!'); -->
<script src="data:text/javascript;base64,YWxlcnQoJ0hlbGxvIHdvcmxkIScpOw=="
        defer></script>

<script>
  alert('Buh-bye world!');
</script>

手动完成它有点费力,因此如果您有幸以某种方式(Handlebars、Angular 等)编译 HTML,那么这将有很大帮助。

我目前正在使用:

<script src="data:text/javascript;base64,{{base64 "alert('Hello world!');"}}"
        defer>
</script>
不需要base64编码,只需使用这个:data:text/javascript,alert('hello world')大多数人没有意识到 dataURL 本质上不需要编码为 base64。工作示例:jsbin.com/vazuruxica/edit?html,output我建议对您的答案进行编辑:)
2021-04-30 18:17:59
这超出了 eval;错误不仅很难找到,还必须阅读 base64 :)
2021-05-15 18:17:59

来自 MDN 文档:

defer
这个布尔属性设置为向浏览器指示脚本将在文档被解析之后执行,但在触发之前执行DOMContentLoadeddefer 属性只能用于外部脚本。

这称为 IIFE (立即调用的函数表达式),它在 DOM 可用之前执行。因此,在这种情况下jQuery是未定义的,因为它不在 DOM 中。

那是因为您拥有 IIFE 语法。它会立即运行并且不会等待 dom 被加载。
2021-04-24 18:17:59
@user31782 因为该(jQuery) 部分需要jQuery在访问之前定义它——并且推迟脚本意味着在其他所有内容(包括内联代码)之后才在该脚本中定义任何内容
2021-04-30 18:17:59
但是如何处理内联脚本呢?为什么他们不遵循声明的顺序?
2021-05-05 18:17:59
在您上次编辑时,它仍然会抛出错误jQuery is not defined
2021-05-14 18:17:59

使用纯文本数据 URI 延迟加载 - Chrome 和 FF

#noLib #vanillaJS

建议不要在跨浏览器生产中使用

直到 MS IE 消亡,MS Edge 将采用 Chromium 开源;)

延迟脚本的唯一方法是外部文件Data_URI(不使用事件 DOMContentLoaded)

推迟

spec script#attr-defer(MDN 网络文档):“如果 src 属性不存在(即对于内联脚本),则不得使用此属性,在这种情况下它将不起作用。)”

数据_URI

规格数据_URI

使用正确的类型“text/javascript”根本不需要base64 ...;)

使用纯文本,因此您可以使用简单的:

<script defer src="data:text/javascript,

//do something with b-lazy plugin, lightbox plugin and then with flexslider

lightbox.option({
  resizeDuration: 200,
  wrapAround: true
})

">

是的,这有点奇怪,但<script type="module">默认情况下被推迟,没有其他选项可以按确切顺序混合以下内容

  • module外部文件 -默认延迟
  • module内联脚本 -默认延迟
  • 外部文件 -可选择延迟
  • 内联脚本 - 只有这个hack - 据我所知(没有库/框架)
非常好的解决方案。这是否适用于所有module浏览器(Edge 非 Chromium 除外)?
2021-04-29 18:17:59

延迟/异步脚本标签不够好

有一个常识,您应该使用<script src=".." async defer>(或script.async = true在分配之前设置src,当您从 JS 执行时)和/或将您的脚本放在页面的最底部,以便尽可能多地加载和呈现页面用户,尽快。

defer.js(注意:我是这个脚本的作者)是用纯 JavaScript 编写的,这使得延迟加载其他内容更加快速和高效您可以有效地延迟任何 javascript 文件以及内联脚本块。

延迟加载 JavaScript

如果您的页面只是使用一些 JavaScript 增强的 HTML 页面,那么您只需使用<script async>. 浏览器解析和执行这些脚本需要时间,每次UI变化都可能重排你的布局,让你的加载速度变慢,没有人喜欢盯着一个空白的页面;用户不耐烦,很快就会离开。

在各种情况下,使用asyncdefer不提供比defer.js更快的页面速度