【问题标题】:In what case can script tags in HTML markups be included in the head tags without using async and defer attributes?在什么情况下,HTML 标记中的脚本标记可以包含在头部标记中而不使用异步和延迟属性?
【发布时间】:2018-12-21 06:27:32
【问题描述】:

大家好,我是 JavaScript 和 Web 开发的新手。我最近遇到了这个关于脚本标签位置的问题。我知道这是一个常见问题,我在 stackoverflow 上查看了一些答案,也在 google 上查看了这个风格指南。但我对这个问题还不是很清楚。

例如,我有一个带有像这样的外部脚本 js 文件的 html 页面

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <script src='js.js'>
    </script>
  </head>
  <body>
  </body>
</html>

js文件是

var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

在我看来,这个 js 文件不依赖于可用的 html 文件的任何 DOM 元素,所以我把这个脚本标签放在哪里应该无关紧要。但事实证明,我必须将此脚本标记放在正文结束标记的底部,否则日期不会按预期显示在页面上。另一种解决方法是在脚本标签中使用 defer 属性,如下所示

<script src='js.js' defer></script>

这让我很困惑,如果一个脚本有任何与 DOM 相关的操作,在我看来,如果没有 defer 或 async 属性,它就不能放在前面的 head 标签内。为什么这个 google 风格指南 https://developers.google.com/speed/docs/insights/BlockingJS 仍然建议我们可以在 head 标签中编写内联脚本,因为访问和操作 DOM 在任何脚本文件中都非常常见。

根据http://caniuse.com/#feat=script-defer,94.59% 的浏览器支持此功能。 94.92% 至少部分支持它。为什么 async 和 defer 属性没有被广泛使用?我的意思是,我查看了很多 HTML 源代码,但我没有在任何地方看到 async 和 defer 属性?

【问题讨论】:

  • 因为我一般用jQuery,相当于forqzy的回答是把你的javascript包裹在ready函数里:$(document).ready(function() { ... })
  • “在我看来,这个 js 文件不依赖于可用的 html 文件的任何 DOM 元素” — 脚本的最后一行非常依赖于document.body 元素,在脚本运行的 head 元素之后创建。
  • 要添加到@Lennholm 的评论中,文档会自上而下逐行加载,这就是尚未创建正文的原因

标签: javascript html web


【解决方案1】:

所以这里有一些解释。

  1. 在没有任何额外属性的情况下在 HTML 中使用 &lt;script&gt;&lt;/script&gt; 将阻止 HTML 解析(换句话说,“将 html 加载到浏览器窗口”)。成功下载后,将获取并执行指定的脚本。之后,将恢复执行(将加载页面)。
  2. 使用&lt;script async&gt;&lt;/script&gt; 允许HTML 解析器在脚本下载之前不会阻止解析。
  3. 使用&lt;script defer&gt;&lt;/script&gt; 可以完全解析HTML,然后执行脚本代码。

因此,从主题中回答您的问题 - 如果 &lt;head&gt;&lt;/head&gt; 中的脚本不需要访问尚不存在的内容,则可以包含(并且将正常工作)。 在您的示例中,您试图将 sth 附加到 body (尚不存在)。 如果您在&lt;head&gt;&lt;/head&gt; 中使用&lt;script async&gt;&lt;/script&gt;,它也不能保证正常工作。例如。脚本很小(几乎可以立即下载)——它将在 html 完全解析之前执行(导致访问尚未存在的内容)。我们可以为异步请求计时,这是它们美丽的一部分。

如果获取的脚本不能直接访问 DOM,则使用异步是有意义的。

使用 defer 是有意义的,例如。如果 JS 文件很大(例如 fetch 需要 5 秒)并且我们想向用户显示某事。在页面上。加载脚本后,我们通过脚本中的 js 将页面更改为它的 inteded 'look'。

请注意,这些并不是异步和延迟的所有用例。

【讨论】:

  • 感谢您的回答!所以基本上使用 可能会导致竞争条件。在我的例子中,当我访问 body DOM 元素时,我应该在脚本标签中使用 defer attr。正确的?鉴于此,@forqzy 下面介绍的技巧是否仍然适用,因为我们可以使用 defer attr 代替?
  • 这取决于您的应用程序是如何设计的以及您使用的是什么技术。如果该站点完全由例如生成。 React 然后您希望在加载和执行脚本后向用户显示 html,而不是显示空白站点,然后执行 js 以填充其内容。
【解决方案2】:

建议你查看这个load and execute order of scripts的答案

正常的方式是在head标签中只加载javascript。 然后在加载整个 html 文件后,在文档中 onload 调用你的函数。

document.addEventListener('DOMContentLoaded', function() {
  console.log('document - loaded - ');

  //call your functions
}, true);

【讨论】:

  • 更准确地说——当 HTML 本身被加载和解析以创建 DOM 时,DOMContentLoaded 事件被触发;它不必等待一切(例如图像)完成加载。此时搜索和操作 DOM 是安全的。
  • 感谢您的回答!在这种情况下,浏览器会下载外部脚本文件并在遇到head标签中的脚本标签时阻止HTML,对吗?我的问题是,将脚本标记放在结束正文标记之前似乎更合理。在这种情况下,我们知道脚本标签只有在 HTML 本身完全加载时才会执行,并且不会阻塞 HTML 的呈现。
  • 虽然这是一种等待 DOM 加载完毕的策略,但这实际上并不能解决有关文档头中脚本的问题。
猜你喜欢
  • 2017-12-30
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-18
  • 2016-03-25
相关资源
最近更新 更多