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 属性,需要考虑兼容性处理。