在前端开发中,JavaScript 的阻塞加载会严重影响网页的性能和用户体验。当浏览器解析 HTML 遇到 <script>
标签时,会停止解析 HTML,优先下载并执行 JavaScript 代码,然后再继续解析 HTML。如果 JavaScript 代码执行时间过长,就会导致页面渲染延迟,出现“白屏”现象。
为了避免 JavaScript 阻塞加载,可以采用以下几种方法:
1. 使用 <script async>
属性:
async
属性告诉浏览器在下载 JavaScript 文件的同时继续解析 HTML。JavaScript 文件下载完成后会立即执行,不会阻塞 HTML 解析。多个带有 async
属性的脚本的执行顺序是不确定的,哪个先下载完哪个先执行。
<script async src="script1.js"></script>
<script async src="script2.js"></script>
2. 使用 <script defer>
属性:
defer
属性也告诉浏览器在下载 JavaScript 文件的同时继续解析 HTML。与 async
不同的是,带有 defer
属性的脚本会在 HTML 解析完成后,DOMContentLoaded 事件触发之前执行。多个带有 defer
属性的脚本会按照它们在 HTML 中出现的顺序执行。
<script defer src="script1.js"></script>
<script defer src="script2.js"></script>
3. 将 <script>
标签放在 <body>
的底部:
将 <script>
标签放在 <body>
的底部,可以确保在执行 JavaScript 代码之前,HTML 的主体内容已经解析完成并渲染出来。这样可以避免 JavaScript 阻塞 HTML 解析,提高用户体验。
<body>
<!-- HTML content -->
<script src="script.js"></script>
</body>
4. 使用动态脚本插入:
通过 JavaScript 动态创建 <script>
元素并将其添加到文档中,可以实现异步加载 JavaScript 文件。
function loadScript(src) {
const script = document.createElement('script');
script.src = src;
script.async = true; // 可选,添加 async 属性
document.head.appendChild(script);
}
loadScript('script1.js');
loadScript('script2.js');
5. 使用 import() 动态导入 (ES Modules):
对于使用 ES Modules 的项目,可以使用 import()
动态导入模块。这允许按需加载 JavaScript 模块,并且是异步的。
import('./myModule.js').then(module => {
// 使用导入的模块
module.doSomething();
});
选择哪种方法?
- 如果脚本之间没有依赖关系,并且不需要访问 DOM,可以使用
async
。 - 如果脚本之间有依赖关系,或者需要访问 DOM,并且需要在 DOMContentLoaded 事件触发之前执行,可以使用
defer
。 - 将
<script>
标签放在<body>
底部是一种简单且有效的方法,适用于小型项目或对性能要求不高的场景。 - 动态脚本插入和
import()
动态导入提供了更大的灵活性,适用于需要按需加载 JavaScript 模块的场景。
通过以上方法,可以有效避免 JavaScript 阻塞加载,提高网页的性能和用户体验。 选择哪种方法取决于你的具体需求和项目规模。 对于现代浏览器,async
和 defer
以及 import()
都是很好的选择。