我应该把<脚本>标签在哪里超文本标记语言?

当在超文本标记语言文档中嵌入JavaScript时,放置<script>标签和包含的JavaScript的合适位置在哪里?我似乎记得你不应该将这些放在<head>部分,但是放置在<body>部分的开头也很糟糕,因为JavaScript必须在页面完全呈现之前被解析(或类似的东西)。这似乎让<body>部分的结束成为<script>标签的逻辑位置。

那么,是放置<script>标签的正确位置吗?

(这个问题参考了这个问题,其中建议JavaScript函数调用应该从<a>标签移动到<script>标签。我专门使用jQuery,但更一般的答案也合适。)

753328 次浏览

如果您使用的是jQuery,那么请将JavaScript代码放在您认为最好的位置,并使用$(document).ready()来确保在执行任何函数之前正确加载内容。

附带说明:我喜欢<head>部分中的所有脚本标签,因为这似乎是最干净的地方。

就在结束主体标签之前,如将脚本放在底部所述:

将脚本放在底部

脚本造成的问题是它们会阻止并行下载。HTTP/1.1规范建议浏览器每个主机名并行下载的组件不超过两个。如果您从多个主机名提供图像,您可以获得两个以上的并行下载。然而,当脚本正在下载时,浏览器不会启动任何其他下载,即使在不同的主机名上。

最保守(也被广泛接受)的答案是“在结束标记之前的底部”,因为在开始执行任何东西之前,整个DOM都将被加载。

出于各种原因,有些人持不同意见,他们从可用的实践开始,故意以页面加载事件开始执行。

事实证明,它可以无处不在。

您可以使用jQuery之类的东西推迟执行,因此它放置在哪里并不重要(除了解析期间的小性能影响)。

根据脚本及其使用情况,最好的(就页面加载和渲染时间而言)可能不是使用传统的-标记本身,而是动态触发脚本的异步加载。

有一些不同的技术,但最直接的是在触发window.onload事件时使用document.create元素(“脚本”)。然后在页面本身呈现时首先加载脚本,从而不影响用户等待页面出现的时间。

这自然要求脚本本身不需要用于呈现页面。

有关更多信息,请参阅Steve Souders(YSlow的创建者,但现在在Google)的帖子异步脚本的耦合

标准建议,由Yahoo!ExceptionalPerformance团队推广,是将<script>标记放在文档正文的末尾,这样它们就不会阻止页面的呈现。

但是有一些较新的方法可以提供更好的性能,如这个答案中关于Google Analytics JavaScript文件加载时间的描述:

Steve Souders(客户端性能专家)有一些关于以下内容的很棒的幻灯片

  • 并行加载外部JavaScript文件的不同技术
  • 它们对加载时间和页面渲染的影响
  • 浏览器显示什么样的“正在进行”指示器(例如状态栏中的“加载”、沙漏鼠标光标)。

这要看情况而定。如果您正在加载一个脚本,该脚本需要为您的页面设置样式/在您的页面中使用操作(例如单击按钮),那么您最好将其放在顶部。如果您的样式是100%CSS并且您有按钮操作的所有后备选项,那么您可以将其放在底部。

或者最好的办法(如果这不是问题的话)是您可以制作一个模态加载框,将您的JavaScript代码放在页面底部,并在脚本的最后一行加载时使其消失。这样您就可以避免用户在脚本加载之前在您的页面中使用操作。也可以避免不正确的样式。

以下是当浏览器加载带有<script>标签的网站时会发生的情况:

  1. 获取超文本标记语言页面(例如index.html
  2. 开始解析超文本标记语言
  3. 解析器遇到引用外部脚本文件的<script>标记。
  4. 浏览器请求脚本文件。同时,解析器阻止并停止解析页面上的其他超文本标记语言。
  5. 一段时间后,脚本被下载并随后执行。
  6. 解析器继续解析超文本标记语言文档的其余部分。

步骤#4会导致糟糕的用户体验。您的网站基本上停止加载,直到您下载了所有脚本。如果用户讨厌一件事,那就是等待网站加载。

为什么会发生这种事?

任何脚本都可以通过document.write()或其他DOM操作插入自己的超文本标记语言。这意味着解析器必须等到脚本下载并执行后才能安全地解析文档的其余部分。毕竟,脚本可以已经在文档中插入了自己的超文本标记语言。

但是,大多数JavaScript开发人员不再操作文档正在加载的DOM。相反,他们等到文档加载完毕后再修改它。例如:

<!-- index.html --><html><head><title>My Page</title><script src="my-script.js"></script></head><body><div id="user-greeting">Welcome back, user</div></body></html>

JavaScript:

// my-script.jsdocument.addEventListener("DOMContentLoaded", function() {// this function runs when the DOM is ready, i.e. when the document has been parseddocument.getElementById("user-greeting").textContent = "Welcome back, Bart";});

因为您的浏览器不知道my-script.js在下载并执行文档之前不会修改文档,所以解析器停止解析。

过时的建议

解决这个问题的旧方法是将<script>标记放在<body>的底部,因为这确保解析器直到最后都不会被阻塞。

这种方法有自己的问题:在解析整个文档之前,浏览器无法开始下载脚本。对于具有大型脚本和样式表的大型网站,能够尽快下载脚本对性能非常重要。如果您的网站在2秒内没有加载,人们就会去另一个网站。

在最佳解决方案中,浏览器将尽快开始下载您的脚本,同时解析文档的其余部分。

现代方法

今天,浏览器支持脚本的asyncdefer属性。这些属性告诉浏览器在下载脚本时继续解析是安全的。

async

<script src="path/to/script1.js" async></script><script src="path/to/script2.js" async></script>

带有async属性的脚本是异步执行的。这意味着脚本在下载后立即执行,同时不会阻塞浏览器。这意味着脚本2可能在脚本1之前下载并执行。

根据http://caniuse.com/#feat=script-async,97.78%的浏览器支持这一点。

推迟

<script src="path/to/script1.js" defer></script><script src="path/to/script2.js" defer></script>

带有deler属性的脚本按顺序执行(即先执行脚本1,然后执行脚本2)。这也不会阻止浏览器。

与异步脚本不同,延迟脚本仅在加载整个文档后执行。

根据http://caniuse.com/#feat=script-defer,97.79%的浏览器支持这一点。98.06%至少部分支持它。

关于浏览器兼容性的重要说明:在某些情况下,Internet Explorer 9及更早版本可能会无序执行延迟脚本。如果您需要支持这些浏览器,请先阅读这个

(要了解更多信息并查看async、def和普通脚本之间差异的一些非常有用的可视化表示,请查看此答案参考部分的前两个链接)

结论

当前最先进的技术是将脚本放在<head>标签中并使用asyncdefer属性。这允许您的脚本尽快下载而不会阻止您的浏览器。

好消息是,您的网站仍然应该正确加载2%不支持这些属性的浏览器,同时加快其他98%。

参考文献

非阻塞脚本标签可以放置在任何地方:

<script src="script.js" async></script><script src="script.js" defer></script><script src="script.js" async defer></script>
  • #0脚本将在可用时立即异步执行
  • #0脚本在文档完成解析时执行
  • 如果不支持async,#0脚本会回退到延迟行为

这样的脚本将在文档准备好后异步执行,这意味着您不能这样做:

<script src="jquery.js" async></script><script>jQuery(something);</script><!--* might throw "jQuery is not defined" error* defer will not work either-->

或者这个:

<script src="document.write(something).js" async></script><!--* might issue "cannot write into document from an asynchronous script" warning* defer will not work either-->

或者这个:

<script src="jquery.js" async></script><script src="jQuery(something).js" async></script><!--* might throw "jQuery is not defined" error (no guarantee which script runs first)* defer will work in sane browsers-->

或者这个:

<script src="document.getElementById(header).js" async></script><div id="header"></div><!--* might not locate #header (script could fire before parser looks at the next line)* defer will work in sane browsers-->

话虽如此,异步脚本提供了这些优势:

  • 并行下载资源
    浏览器可以并行下载样式表、图像和其他脚本,而无需等待脚本下载和执行。
  • 源序独立性
    您可以将脚本放在head或body中,而不必担心阻塞(如果您使用的是CMS,则很有用)。执行顺序仍然很重要。

可以通过使用支持回调的外部脚本来规避执行顺序问题。许多第三方JavaScript API现在支持非阻塞执行。这是异步加载Google Maps API的示例。

脚本阻塞DOM加载,直到它被加载和执行。

如果您将脚本放在<body>的末尾,所有DOM都有机会加载和渲染(页面将“显示”得更快)。<script>将有权访问所有这些DOM元素。

另一方面,将其放置在<body> start或更高版本之后将执行脚本(其中仍然没有任何DOM元素)。

您包含jQuery,这意味着您可以将其放置在您希望的任何地方并使用.就绪()

  • 如果您仍然非常关心Internet Explorer在版本10之前的支持和性能,最好总是使您的脚本标记成为超文本标记语言主体的最后一个标记。这样,您可以确定DOM的其余部分已经加载,您不会阻止和渲染。

  • 如果您不太关心版本10之前的Internet Explorer,您可能希望将脚本放在文档的头部并使用defer来确保它们仅在加载DOM后运行(<script type="text/javascript" src="path/to/script1.js" defer></script>)。如果您仍然希望您的代码在版本10之前的Internet Explorer中工作,请不要忘记将您的代码包装在window.onload中!

    <script src="myjs.js"></script></body>

脚本标签应始终在身体接近之前或超文本标记语言文件处使用。

页面将加载超文本标记语言和CSS,稍后将加载JavaScript。

如果需要,请检查此项:

http://stevesouders.com/hpws/rule-js-bottom.php

我认为这取决于网页的执行。

如果您要显示的页面在不先加载JavaScript的情况下无法正常显示,那么您应该首先包含JavaScript文件。

但是,如果您可以在不首先下载JavaScript文件的情况下显示/渲染网页,那么您应该将JavaScript代码放在页面底部。因为它将模拟快速的页面加载,并且从用户的角度来看,页面加载速度似乎更快。

您可以将大多数<script>引用放在<body>的末尾。

但是如果您的页面上有使用外部脚本的活动组件,那么它们的依赖项(. js文件)应该在此之前(理想情况下在标记中)。

编写JavaScript代码的最佳位置是在</body>标记之后或之前的文档末尾,以便首先加载文档,然后执行JavaScript代码。

<script> ... your code here ... </script></body>

如果您在jQuery中写入,以下内容可以在head文档中,它将在文档加载后执行:

<script>$(document).ready(function(){// Your code here...});</script>

在末尾包含脚本主要用于首先显示网页的内容/样式的地方。

在头部中包含脚本会尽早加载脚本,并且可以在加载整个网页之前使用。

如果最后输入脚本,则只有在加载整个样式和设计后才会进行验证,这对于快速响应的网站来说是不受欢迎的。

2019年的现代方法是使用ES6模块类型脚本

<script type="module" src="..."></script>

默认情况下,模块是异步加载和延迟的。即,您可以将它们放置在任何地方,它们将并行加载并在页面完成加载时执行。

这里描述了脚本和模块之间的区别:

JavaScript中的经典脚本与模块脚本

与脚本相比,模块的执行在这里描述:

模块默认延迟

支持显示在这里:

我可以使用…支持HTML5、CSS3等表格吗

放置<script>标记的最好位置是在关闭</body>标记之前,因此下载和执行它不会阻止浏览器解析文档中的超文本标记语言。

外部加载JavaScript文件也有自己的优势,比如它可以被浏览器缓存,可以加快页面加载时间,它分离了超文本标记语言和JavaScript代码,有助于更好地管理代码库。

但现代浏览器也支持一些其他最佳方式,例如asyncdefer来加载外部JavaScript文件。

异步和延迟

正常情况下,超文本标记语言页面执行会逐行开始。当遇到外部JavaScript<script>元素时,会停止超文本标记语言解析,直到JavaScript下载并准备好执行。可以使用deferasync属性更改此正常页面执行。

Defer

当使用延迟属性时,JavaScript与超文本标记语言解析并行下载,但只有在完成完整的超文本标记语言解析后才会执行。

<script src="/local-js-path/myScript.js" defer></script>

Async

当使用async属性时,JavaScript会在遇到脚本时立即下载,下载后,它将与超文本标记语言解析一起异步(并行)执行。

<script src="/local-js-path/myScript.js" async></script>

何时使用哪些属性

  • 如果您的脚本独立于其他脚本并且是模块化的,请使用async
  • 如果您使用async加载script1和script2,两者都将运行同时进行超文本标记语言解析,一旦下载并且可用。
  • 如果您的脚本依赖于另一个脚本,则对两者都使用defer
  • 当script1和script2按defer的顺序加载时,保证script1首先执行,
  • 然后script2将在script1完全执行后执行。
  • 如果script2依赖于script1,则必须这样做。
  • 如果您的脚本足够小并且依赖于另一个脚本async类型,然后使用没有属性的脚本并将其置于所有async脚本之上。

参考:外部JavaScript JS文件-优点,缺点,语法,属性

您可以通过使用围绕JavaScript代码的专用超文本标记语言<script>在超文本标记语言文档中添加JavaScript代码。

<script>标记可以放在超文本标记语言的<head>部分、<body>部分或</body>关闭标记之后,具体取决于您希望JavaScript加载的时间。

通常,JavaScript代码可以进入文档<head>部分,以便将它们包含在超文本标记语言文档的主要内容之外。

但是,如果您的脚本需要在页面布局中的某个位置运行-例如使用document.write生成内容时-您应该将其放在应该调用它的位置,通常是在<body>部分中。

总是,我们必须把脚本放在结束的body标记之前,期待一些特定的场景。

例如:

`<html> <body> <script> document.getElementById("demo").innerHTML = "Hello JavaScript!"; </script> </body> </html>`

最好把它放在</body>结束标记之前。

为什么?官方文档:https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics#a_hello_world!_example

注意:说明(上面)放置元素的原因靠近超文本标记语言文件底部的是浏览器读取它出现在文件中的顺序。

如果JavaScript首先加载并且它应该影响超文本标记语言尚未加载的,可能存在问题。放置JavaScript靠近超文本标记语言页面的底部是适应这种情况的一种方法要了解有关替代方法的更多信息,请参阅脚本加载策略。