本文深入探讨 JavaScript 文件的加载策略对页面性能的影响。从将脚本置于 body 底部以减少渲染阻塞,到合并文件以降低 HTTP 请求次数,再到利用动态脚本注入实现无阻塞加载。文章对比了不同场景下的执行顺序与兼容性处理,旨在提升网页加载速度与用户体验。
从事前端开发一年多,积累了一些关于前端开发的见解,特此整理分享。
前端开发离不开各种工具的支持,例如我常用的 Google 浏览器以及用于抓包分析的 Fiddler。
原始情况与问题分析
一:原始情况
首先观察以下代码结构:
代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JsLoad.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head">
<title></title>
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
<script src="jquery/jquery-1.4.1.js" type="text/javascript"></script>
<script src="js/hello.js" type="text/javascript"></script>
<script src="js/world.js" type="text/javascript"></script>
</head>
<body>
<img src="1.jpg" width="200" height="300" />
</body>
</html>
大约 90% 的开发者会将 JavaScript 文件放置在 head 标签内,但我们需要深入思考其后果。多数浏览器使用单线程同时处理“界面 UI 更新”和“JS 脚本执行”。
当解析器遇到 <script> 标签时,页面的下载和渲染过程必须暂停,直到该脚本执行完毕。这对用户而言体验极差,面对锁定的页面,
用户极有可能直接关闭页面。

通过上述瀑布图分析,我们可以发现两个关键点:
第一:
尽管三个 JS 文件并行下载,但根据前述理论,JS 脚本应串行执行。值得注意的是,IE8、Firefox3.5 和 Chrome2 均实现了 JS 的并行下载,
这是一个显著的进步,但它仍然会阻碍其他资源(如图片)的下载。
第二:
图片 1.jpg 的下载是在 JS 执行完成后才触发的,这证实了上述观点:JS 加载阻塞了图片资源的加载。
第一步优化:调整脚本位置
二:第一步优化
既然 JS 会阻塞 UI 渲染,我们可以尝试将 JS 文件移至 </body> 标签之前。这样可以确保 <script> 之前的 HTML 内容能够完整呈现,避免用户看到页面空白等待
的情况,从而提升页面的友好度。
代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JsLoad.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head">
<title></title>
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<img src="1.jpg" width="200" height="300" />
<script src="jquery/jquery-1.4.1.js" type="text/javascript"></script>
<script src="js/hello.js" type="text/javascript"></script>
<script src="js/world.js" type="text/javascript"></script>
</body>
</html>
下方的图表显示,1.jpg 和三个 JS 文件几乎可以并行下载和执行。总耗时由之前的“469ms+”缩短至“326ms”。

第二步优化:减少 HTTP 请求
三:第二步优化
观察上述“瀑布图”,可以发现三个 JS 文件触发了三次“Get”请求。众所周知,HTTP Get 请求需要携带 HTTP 头信息,
这必然消耗额外时间。因此,我们的优化方案是减少 Get 请求次数。通常有两种实现方式。
第一种:合并 JS 文件,例如将上述的“hello.js”和“world.js”合并为一个文件。
第二种:利用第三方工具,例如 PHP 中的 Minify。
关于第二种做法,淘宝应用较多。查看其中的一个 script 标签,它应用了三个 JS 文件,但由 3 个 Get 请求变为 1 个。

第三步优化:无阻塞加载策略
四:第三步优化
无论是将 JS 文件置于页面底部,还是将多个文件合并为一个,其本质仍属于“阻塞模式”,即锁定浏览器。随着 Web 页面日益复杂,JS 文件数量增多,
这依然令人头疼。因此,我们提倡采用“无阻塞模式”加载 JS 脚本,即在页面完全呈现后再追加 JS,这对应着在 window.onload 事件触发后,
再追加 JS。这就是所谓的“无阻塞”加载,但其中有一个非常关键的注意事项:我们需要明确对 JS 加载顺序是否有严格要求。
第一种情况:无顺序要求。例如,如果“hello.js”和“world.js”之间没有依赖关系,我们可以完全使用 jQuery 动态追加来实现。
代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JsLoad.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head">
<title></title>
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<img src="1.jpg" width="200" height="300" />
<script src="jquery/jquery-1.4.1.js" type="text/javascript">
<script type="text/javascript">
window.onload = function () {
$("#head").append("<script src='js/hello.js' type='text/javascript'><\/script>")
$("#head").append("<script src='js/world.js' type='text/javascript'><\/script>");
}
</script>
</body>
</html>

从图中可以看出,“hello.js”和“world.js”出现在蓝色线之后,说明这两个 JS 文件是在 DOMContentLoaded 结束后才触发加载的,这样就不会造成页面锁定
等待。
第二种情况:有顺序要求
为什么必须考虑顺序要求?对于上述动态追加的“两个 JS”文件,在 IE 系列浏览器中,无法保证 hello.js 一定在 world.js 之前执行,
它只会按照服务器端返回的顺序执行代码。
代码如下:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="JsLoad.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head">
<title></title>
<link href="Styles/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<img src="1.jpg" width="200" height="300" />
<script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
//IE
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
}
} else {
//非IE
script.onload = function () {
callback();
}
}
script.src = url;
document.getElementById("head").appendChild(script);
}
//第一步加载jquery类库
loadScript("jquery/jquery-1.4.1.js", function () {
//第二步加载hello.js
loadScript("js/hello.js", function () {
//第三步加载world.js
loadScript("js/world.js", function () {
});
});
});
</script>
</body>
</html>

大家也能看到,页面完全 Load 的时间其实也就 310ms 左右,大大提高了网页的下载呈现和友好型。
同样也可以看看腾讯网,他也是这么干的。

是不是非常的有用呢,本人这里也是花了些时日来做这些研究测试,希望小伙伴们能看到心里去,毕竟这也是“麻花藤”公司的解决方案,大家参考下吧