以下是浏览器加载带有标记的网站时发生的情况:<script>
- 获取 HTML 页面(例如索引.html)
- 开始解析 HTML
- 解析器遇到引用外部脚本文件的标记。
<script>
- 浏览器请求脚本文件。同时,解析器会阻止并停止解析页面上的其他 HTML。
- 一段时间后,脚本被下载并随后执行。
- 解析器继续解析 HTML 文档的其余部分。
步骤#4会导致糟糕的用户体验。您的网站基本上停止加载,直到您下载了所有脚本。如果有一件事是用户讨厌的,那就是等待网站加载。
为什么会发生这种情况?
任何脚本都可以通过或其他 DOM 操作插入自己的 HTML。这意味着解析器必须等到脚本下载并执行完毕后才能安全地解析文档的其余部分。毕竟,脚本可以在文档中插入自己的HTML。document.write()
但是,大多数 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.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 src="path/to/script1.js" async></script>
<script src="path/to/script2.js" async></script>
具有异步属性的脚本以异步方式执行。这意味着脚本在下载后立即执行,在此期间不会阻止浏览器。这意味着脚本 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>
具有 defer 属性的脚本按顺序执行(即第一个脚本 1,然后是脚本 2)。这也不会阻止浏览器。
与异步脚本不同,延迟脚本仅在加载整个文档后执行。
根据 http://caniuse.com/#feat=script-defer,97.79%的浏览器都支持此功能。98.06%的人至少部分支持它。
有关浏览器兼容性的重要说明:在某些情况下,Internet Explorer 9 及更早版本可能会无序执行延迟脚本。如果您需要支持这些浏览器,请先阅读本文!
(要了解更多信息并查看异步,延迟和普通脚本之间差异的一些非常有用的可视化表示,请查看本答案的参考部分中的前两个链接)
结论
当前最先进的方法是将脚本放在标记中并使用 or 属性。这允许您的脚本尽快下载,而不会阻止您的浏览器。<head>
async
defer
好消息是,您的网站仍然应该在不支持这些属性的2%的浏览器上正确加载,同时加快其他98%的速度。
引用