题 我应该在哪里将

在JavaScript文档中嵌入JavaScript时,放在哪里的适当位置 <script> 标签和包含JavaScript?我似乎记得你不应该把它们放在 <head> 部分,但放在开头 <body> 部分也很糟糕,因为在完全呈现页面之前必须解析JavaScript(或类似的东西)。这似乎离开了 结束 的 <body> 部分作为一个合理的地方 <script> 标签。

那么,在哪里  适合放置的地方 <script> 标签?

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


1162
2018-01-12 18:15


起源


可能重复: stackoverflow.com/questions/143486/... - Mirko Cianfarani


答案:


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

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

步骤#4会导致糟糕的用户体验。在您下载完所有脚本之前,您的网站基本上会停止加载。如果有一件事是用户讨厌它正在等待网站加载。

为什么会发生这种情况?

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

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

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

使用Javascript:

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

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

过时的推荐

解决这个问题的旧方法是 <script> 标签在你的底部 <body>,因为这可以确保解析器直到最后才被阻止。

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

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

现代的方法

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

异步

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

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

根据 http://caniuse.com/#feat=script-async,94.57%的浏览器支持此功能。

延缓

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

具有延迟属性的脚本按顺序执行(即第一个脚本1,然后是脚本2)。这也不会阻止浏览器。

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

根据 http://caniuse.com/#feat=script-defer,94.59%的浏览器支持此功能。 94.92%至少部分支持它。

关于浏览器兼容性的重要说明:在某些情况下,IE <= 9可能会无序执行延迟脚本。如果您需要支持这些浏览器,请阅读 这个 第一!

结论

目前最先进的技术是将脚本放入 <head> 标记并使用 async 要么 defer 属性。这样可以在不阻止浏览器的情况下尽快下载脚本。

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


1471
2018-06-05 21:20



我很惊讶没有人引用谷歌的解释...... developers.google.com/speed/docs/insights/BlockingJS - Casey Falk
我不清楚什么触及DOM,什么不是。你能澄清一下吗?在像jquery.js这样的东西上进行异步加载是否安全? - Doug
@Doug例如 document.write 在dom上运作。问题不是 如果 一个脚本操纵dom,但是 什么时候 它确实。只要所有dom操纵发生在之后 domready 事件已经触发,你没事。 jQuery是一个库,因此不会 - 或者不应该 - 自己操纵dom。 - Bart
这个答案有误导性。现代浏览器在到达可能影响HTML的同步脚本标记时不会停止解析,它们只是停止呈现/执行,并继续乐观地解析以开始下载其他资源,如果没有HTML受到影响,这些资源可能会随后被请求。 - Fabio Beltramini
为什么 async 和 defer 属性是不是没有使用?我的意思是,我从互联网上看过很多HTML资源,我看不到 async 和 defer 属性在哪里。 ......? - john c. j.


就在关闭身体标签之前,如上所述

http://developer.yahoo.com/performance/rules.html#js_bottom

把脚本放在底部

脚本引起的问题是它们阻止了并行下载。 HTTP / 1.1规范建议浏览器每个主机名并行下载不超过两个组件。如果您从多个主机名提供图像,则可以并行执行两次以上的下载。但是,在下载脚本时,即使在不同的主机名上,浏览器也不会启动任何其他下载。


218
2018-01-12 18:18



同意这个概念及其解释。但是如果用户开始玩该页面会发生什么。假设我有一个AJAX下拉列表,它会在页面出现给用户后开始加载,但在加载时,用户会点击它!如果“真正不耐烦”的用户提交表单怎么办? - Hemant Tank
@Hermant旧评论,但您可以默认禁用字段,然后在DOM完全加载时使用JS启用它们。这就是Facebook现在似乎在做的事情。 - novato
刚用chrome测试过,检查一下是否仍然相同。它是。您可以在此处查看浏览器的页面加载时间差异。 stevesouders.com/cuzillion - cypher
如果这是最佳实践,为什么堆栈溢出包括<head>中的所有脚本标记? :-P - Philip
在某些情况下,特别是在ajax重型站点,装载头部实际上可以加快加载时间。看到: encosia.com/dont-let-jquerys-document-ready-slow-you-down (请注意,“live()”函数在jquery中已弃用,但该文章仍适用于“on()”或“delegate”函数)。也可能需要加载<head>以保证正确的行为,如@Hermant所指出的那样。最后, modernizr.com/docs 建议将其脚本放在<head>中,原因在其网站上有说明。 - Nathan


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

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

这样的脚本将在文档就绪后异步执行,这意味着你不能这样做:

<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
-->

话虽如此,异步脚本提供了以下优势:

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

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


56
2018-02-06 11:19



这是今天的正确答案 - 使用这种方法意味着更容易保持您的小部件自包含,不需要花哨 <head> 包括逻辑。 - Daniel Sokolowski
我很困惑为什么你不能使用 async 要么 defer 当您在第二个块中指定jQuery时: <script src="jquery.js" async></script>。你能解释一下原因吗?我认为我需要根据接受的答案获得性能的异步标记 - 所以即使在jQuery仍在加载时我的页面也可以加载]。谢谢! - elbowlobstercowstand
@elbow 99%的次数 <script src=jquery.js> 接下来是 $(function(){ ... }) 阻止页面中的某个地方。异步加载并不能保证在浏览器尝试解析这些块时会加载jQuery,因此它会引发 $未定义 错误(如果从缓存加载jQuery,则可能无法获得错误)。我回答了一个关于异步加载jQuery并保留的问题 $(function(){ ... })。我会看是否能找到它,或者你可以看看这个问题: stackoverflow.com/q/14811471/87015 - Salman A
@SalmanA谢谢!是的,我达到了99%。我首先需要 jquery lib加载, 然后 我剩下的 .js 脚本。当我宣布 async 要么 defer 在...上 jquery lib脚本标签,我的 .js 脚本不起作用。我想 $(function(){ ... })保护 - 猜测不。 当前解决方案 我不添加 defer 要么 async 上 jquery lib脚本,但我确实添加了 async 在我的跟进 .js 脚本。注意:我正在做的原因 任何 这是为了让Google Page Speed感到高兴。再次感谢您的帮助!欢迎任何其他建议。 (或者您之前回答的链接)。 :) - elbowlobstercowstand
@elbow见 stackoverflow.com/a/21013975/87015,它只会给你一个想法,但不是完整的解决方案。您可以改为搜索“jquery异步加载程序库”。 - Salman A


雅虎推出的标准建议!卓越的性能团队,就是把它 <script> 文档正文末尾的标记,因此它们不会阻止页面的呈现。

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

有一些 很棒的幻灯片 作者:Steve Souders(客户端性能专家)关于:

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

36
2018-01-12 23:37





如果您正在使用JQuery,那么将javascript放在最适合您的地方并使用 $(document).ready() 确保在执行任何功能之前正确加载东西。

旁注:我喜欢我的所有脚本标签 <head> 那段似乎是最干净的地方。


19
2018-01-12 18:18



在头上......呃? <header>? - Dan Lugg
注意使用 $(document).ready() 并不意味着你可以把你的JavaScript 随地 你喜欢 - 你仍然需要把它放在后面 <script src=".../jquery.min.js"> 你在哪里包含jQuery,所以 $ 存在。 - Rory O'Kane
将脚本标记放在<head>部分中并不是最佳选择 - 这将延迟显示页面的可见部分,直到加载脚本为止。 - CyberMonk
不,@丹,a header element是HTML文档内容的一部分,应该在一次或多次内发生 body 元件. The head`标签用于文档的元数据和非内容数据。这些天,现在是 defer 和 async 脚本标签的理想位置。 header 元素应仅包含描述其后的文档部分的信息。 - ProfK
@ProfK,Dan在四年前发布这个问题时指的是未经编辑的原始问题。如您所见,问题是在一年后编辑的。 - kojow7


XHTML不会验证脚本是否在head元素之外的任何位置。 原来它可以无处不在。

您可以使用类似jQuery的命令来延迟执行,因此放置它的位置并不重要(除了解析期间的小性能命中)。


8
2018-01-12 18:22



XHTML将使用正文中的脚本标记进行验证,包括严格标签和过渡标签。然而,样式标签可能只在头部。 - I.devries


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

脚本标签应该始终使用 身体关闭 要么 HTML中的底部 文件。

然后你可以在加载之前先看到页面的内容 JS 文件。

如果需要,检查这个: http://stevesouders.com/hpws/rule-js-bottom.php


8
2017-07-16 11:16



这实际上回答了这个问题。我想知道几乎所有发布的例子都没有给出“页面结尾”的正确视觉背景 - Ken Ingram
这个答案非常具有误导性,而且很可能是错误的。中的文章 谷歌 并在 MDN 表明同步JS(这里就是这种情况)总是阻止DOM构造和解析,这将导致首次渲染延迟。因此,在获取JS文件并完成执行之前,您无法看到页面的内容,无论您将JS文件放在HTML文档中的哪个位置,只要它是同步的 - Lingaraju E V
它还引用了2009年提出的不再相关的观点。 - toxaq


传统(并且被广泛接受)的答案是“在底部”,因为在任何事情开始执行之前,整个DOM都将被加载。

出于各种原因,有异议者从可用的练习开始,故意开始执行页面onload事件。


5
2018-01-12 18:18





根据脚本及其用法,尽可能最好(在页面加载和渲染时间方面)可能不使用传统的<script> -tag本身,而是动态地异步加载脚本。

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

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

有关更多信息,请参阅帖子 耦合异步脚本 作者:Steve Souders(YSlow的创始人,但现在在谷歌)。


0
2018-01-12 21:06