HTML中script标签应放置的位置
HTML中script标签应放置的位置
技术背景
在浏览器加载包含 <script>
标签的网站时,会按特定顺序执行操作。通常是先获取HTML页面,开始解析HTML,当解析器遇到引用外部脚本文件的 <script>
标签(无 defer
或 async
属性且非 type
为 module
的情况),浏览器会请求该脚本文件,同时解析器会阻塞并停止解析页面上的其他HTML,直到脚本下载并执行完成后,才会继续解析剩余的HTML文档。这种机制会导致用户体验不佳,因为在所有脚本下载完成前,网站基本处于停止加载状态。
实现步骤
传统放置方式
早期解决上述问题的方法是将 <script>
标签放在 <body>
的底部,这样能确保解析器直到最后才被阻塞。示例如下:
1 |
|
但这种方式存在问题,浏览器要等到整个文档解析完成后才能开始下载脚本,对于有大量脚本和样式表的大型网站,尽早下载脚本对性能至关重要。
现代放置方式
如今,浏览器支持在脚本上使用 async
和 defer
属性,这些属性告知浏览器在下载脚本时可以安全地继续解析文档。
async属性
使用 async
属性的脚本会异步执行,即脚本下载完成后立即执行,不会阻塞浏览器。示例如下:
1 |
|
不过,使用 async
属性无法保证脚本的执行顺序,可能会出现 script2
在 script1
之前下载并执行的情况。
defer属性
使用 defer
属性的脚本会按顺序执行,且不会阻塞浏览器。与 async
脚本不同,defer
脚本只有在整个文档加载完成后才会执行。示例如下:
1 |
|
module类型
包含 type="module"
的脚本将被视为JavaScript模块,并像 defer
脚本一样加载。示例如下:
1 |
|
核心代码
传统方式代码示例
1 |
|
async属性代码示例
1 |
|
defer属性代码示例
1 |
|
module类型代码示例
1 |
|
最佳实践
目前的最佳实践是将脚本放在 <head>
标签中,并使用 async
或 defer
属性,这样可以让脚本尽快下载,同时不阻塞浏览器。这样即使在不支持这些属性的2%的浏览器上,网站也能正常加载,而在其他98%的浏览器上可以加快加载速度。
常见问题
传统方式的问题
将 <script>
标签放在 <body>
底部时,浏览器要等到整个文档解析完成后才能开始下载脚本,对于大型网站可能会影响性能。
async属性的问题
使用 async
属性无法保证脚本的执行顺序,可能会导致依赖关系出错。例如:
1 |
|
可能会抛出 jQuery is not defined
错误。
defer属性的问题
虽然 defer
属性可以保证脚本按顺序执行,但如果脚本依赖于页面加载过程中的某些操作,可能会出现问题。
兼容性问题
部分旧版本浏览器可能不支持 async
和 defer
属性,需要考虑兼容性处理。