在HTML文档中嵌入JavaScript时,在哪里放置<script>标记和包含的JavaScript?我似乎记得,你不应该把这些放在<head>部分,但放在<body>部分的开头也是不好的,因为JavaScript必须在页面完全呈现之前进行解析(或类似的)。这似乎将<body>部分的结尾作为<script>标记的逻辑位置。

那么,哪里是放置<script>标记的正确位置?

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


当前回答

现代方法是使用ES6“模块”类型脚本。

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

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

进一步阅读:

脚本和模块之间的区别与脚本相比,模块的执行被延迟(默认情况下模块被延迟)ES6模块的浏览器支持

其他回答

如果您仍然非常关心Internet Explorer版本10之前的支持和性能,那么最好始终将脚本标记设置为HTML正文的最后一个标记。这样,您就可以确定DOM的其余部分已经加载,并且不会阻塞和渲染。如果您不太关心版本10之前的Internet Explorer,您可能希望将脚本放在文档的开头,并使用defer来确保它们仅在加载DOM后运行(<script type=“text/javascript”src=“path/to/script1.js”defer></script>)。如果您仍然希望代码在版本10之前的Internet Explorer中运行,请不要忘记将代码包装在窗口中!

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

此外,从外部加载JavaScript文件也有其自身的优点,比如它将被浏览器缓存,可以加快页面加载时间,它将HTML和JavaScript代码分开,有助于更好地管理代码库。

但现代浏览器还支持其他一些最佳方式,如异步和延迟加载外部JavaScript文件。

异步和延迟

通常HTML页面的执行是逐行开始的。当遇到外部JavaScript<script>元素时,HTML解析将停止,直到下载JavaScript并准备好执行。可以使用defer和async属性更改正常的页面执行。

推迟

当使用defer属性时,JavaScript将与HTML解析并行下载,但只有在完成完整的HTML解析后才能执行。

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

异步

当使用async属性时,一旦遇到脚本,就会立即下载JavaScript,在下载之后,它将与HTML解析一起异步(并行)执行。

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

何时使用哪些属性

如果您的脚本独立于其他脚本并且是模块化的,请使用异步。如果使用异步加载script1和script2,则两者都将运行与HTML解析并行,一旦下载并且可用。如果您的脚本依赖于另一个脚本,则对这两个脚本都使用defer:当script1和script2以延迟的顺序加载时,然后,script2将在script1完全执行后执行。如果script2依赖于script1,则必须执行此操作。如果您的脚本足够小并且依赖于另一个脚本然后使用不带属性的脚本,并将其置于所有异步脚本之上。

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

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

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

如果您在jQuery中编写,以下内容可以在头文档中,并且将在文档加载后执行:

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

非阻塞脚本标记可以放置在任何位置:

<script src="script.js" async></script>
<script src="script.js" defer></script>
<script src="script.js" async defer></script>

异步脚本一旦可用,将立即异步执行当文档完成解析时执行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现在支持非阻塞执行。下面是一个异步加载GoogleMapsAPI的示例。

雅虎推广的标准建议!卓越性能团队,将<script>标记放在文档的<body>元素的末尾,这样它们就不会阻止页面的呈现。

但有一些新的方法可以提供更好的性能,正如我在另一个关于Google Analytics JavaScript文件加载时间的回答中所描述的:

Steve Souders(客户端性能专家)的一些精彩幻灯片介绍了:并行加载外部JavaScript文件的不同技术它们对加载时间和页面呈现的影响浏览器显示什么样的“正在进行”指示符(例如状态栏中的“加载”、沙漏鼠标光标)。