JavaScript文件加载优化策略详解:位置、Defer与Async

作者:安全边界

时间:2026-10-22

浏览:659

本文详细解析JavaScript文件加载的三种优化方案:将脚本置于Body底部、使用Defer属性及Async属性。重点对比了Defer与Async在加载顺序、执行时机及浏览器兼容性(特别是IE9及以下)上的差异,并探讨了动态脚本加载的优缺点及Hack技巧,帮助开发者根据目标环境选择最佳加载策略。

文章目录

JavaScript加载机制与优化概述

在JavaScript引擎的工作机制中,当渲染引擎解析到script标签时,控制权会暂时移交给JS引擎。若script引用的是外部资源,浏览器必须等待资源下载完成后才能执行脚本。因此,针对这一过程存在多种优化空间。

脚本放置位置:Body底部策略

放置在BODY底部

为了使渲染引擎能尽早构建并展示DOM树,通常建议将script标签放置在body元素的末尾。这有助于页面尽快脱离白屏状态,并提前触发DOMContentLoaded事件。然而,在iOS Safari、Android浏览器以及iOS WebView中,即便将JS脚本置于body尾部,上述阻塞现象依然存在。因此,需要采取其他手段来优化JS文件的加载过程。

Defer属性:加载顺序与兼容性处理

DEFER加载

Defer是HTML4中定义的script标签属性,用于指示浏览器:当解析器遇到引用外部资源的script标签时,应暂时挂起脚本执行,继续加载该资源,同时继续解析后续HTML文档。待整个HTML文档解析完毕后,再按顺序执行script中的脚本。


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

其浏览器支持情况为IE9及以下版本。

此外,Defer的执行顺序是严格依赖的,具体表现为:


<script src="outside1.js" defer></script>
<script src="outside2.js" defer></script>

页面解析完成后,脚本会严格按照声明顺序依次执行outside1和outside2文件。

若在IE9以下版本中使用defer,可能会遇到脚本非顺序执行的问题。此时需要通过Hack手段处理,即在两个script标签之间插入一个空的script标签。


<script src="outside1.js" defer></script>
<script></script> //hack
<script src="outside2.js" defer></script>

Async属性:异步加载与执行顺序

ASYNC加载

Async是HTML5新增的script属性,代表另一种JS加载模式。

当渲染引擎解析到带有async属性的script标签时:

浏览器会继续解析剩余HTML文档,同时并行加载script引用的外部资源。

一旦script资源加载完成,浏览器会暂停文档解析,将控制权移交给JS引擎以执行指定脚本。

脚本执行完毕后,浏览器恢复文档解析。

可以看出,async同样能解决加载阻塞问题。但由于其异步执行特性,脚本的执行顺序并不确定,即:


<script src="outside1.js" async></script>
<script src="outside2.js" async></script>

哪个脚本先加载完成,就先执行哪个。因此,对于存在依赖关系的文件,通常不应使用async,而应使用defer。

Defer的兼容性相对较差,主要支持IE9及以上版本。不过,由于该方案多用于移动端环境,这一兼容性问题通常不构成障碍。

动态脚本加载与优化方案

脚本异步

脚本异步加载是许多异步加载库(如RequireJS)的基本原理。具体实现代码如下:


function asyncAdd(src){
  var script = document.createElement('script');
  script.src = src;
  document.head.appendChild(script);
}
//加载js文件
asyncAdd("test.js");

这种方式可以实现文件的异步加载,避免阻塞页面渲染。

然而,这种动态加载方式是无序的,无法正确处理文件间的依赖关系。

因此,我们需要对上述函数进行优化改进。


var asyncAdd = (function(){
  var head = document.head,
    script;
  return function(src){
    script = document.createElement('script');
    script.src= src;
    script.async=false;
    document.head.appendChild(script);
  }
})();
//加载文件
asyncAdd("first.js");
asyncAdd("second.js");
//或者简便一点
["first.js","second.js"].forEach((src)=>{async(src);});

使用脚本异步加载时,通常需要等待CSS文件加载完成后才开始加载JS,这未能充分利用浏览器的并发加载优势。相比之下,使用静态HTML标签加载async或defer则无此限制。

使用脚本异步加载时,只能等待CSS加载完后才会加载JS

使用静态的async加载时,CSS和JS会并发一起加载

方案选择与最佳实践建议

关于这三种方案的取舍,主要取决于项目目标:是需要兼容IE8/9,还是针对手机端,亦或是桌面浏览器,或者是两者的组合。

但在单独使用某一种技巧的场景下,使用时需注意一些关键提示。

JS文件放置位置应该放置到body末尾

如果使用async的话,最后加上defer以求向下兼容


<script src="test.js" async defer></script> //如果两者都支持,async会默认覆盖掉defer
//如果只支持一个,则执行对应的即可

通常,我们使用的加载都是defer加载(因为很强的依赖关系).

以上这篇基于js文件加载优化(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章