高性能Web开发:JS加载阻塞原理与优化策略

作者:听澜

时间:2026-10-24

浏览:643

本文深入解析浏览器中JavaScript加载对页面渲染的阻塞机制,包括外部脚本与嵌入脚本对DOM构建及资源下载的影响。通过分析时间瀑布图案例,揭示嵌入JS导致CSS并行加载失效的根本原因,并提供将脚本置于底部、使用defer属性及避免长耗时函数等具体优化建议,助力提升Web应用加载性能。

文章目录

外部JS的阻塞下载机制

外部JS的阻塞下载


现代浏览器在下载JavaScript文件时,会暂停所有其他活动,包括其他静态资源的下载以及页面内容的呈现。只有当JavaScript文件完成下载、解析并执行完毕后,浏览器才会恢复并行下载其他资源并继续渲染页面。



读者可能会疑惑:为什么JavaScript不能像CSS或图片那样并行下载?这涉及到浏览器构建页面的核心机制,下面将简要介绍。


当浏览器从服务器接收HTML文档并在内存中构建DOM树时,如果遇到对CSS或图片的引用,浏览器会发送请求获取这些资源,同时继续解析后续HTML节点,无需等待请求返回。一旦资源返回,浏览器将其插入DOM树对应位置即可。然而,当遇到JavaScript引用时,浏览器发送请求后会一直等待其返回。这是因为浏览器需要维持稳定的DOM树结构,而JavaScript代码可能直接修改DOM(如使用document.write、appendChild)或改变页面位置(如location.href跳转)。为防止JavaScript修改DOM导致浏览器重新构建DOM树,浏览器会阻塞其他资源的下载和页面呈现。



阻塞下载示意图:下图展示了访问blogjava首页的时间瀑布图。从图中可以看出,开始的两个图片资源是并行下载的,而后面的两个JavaScript文件则是阻塞下载的,即一个接一个地串行下载。


外部JS的阻塞下载机制

嵌入JS与外部JS的阻塞差异

嵌入JS的阻塞下载


嵌入JavaScript指的是直接写在HTML文档中的脚本代码。前文提到引用外部JS会阻塞其后的资源下载和内容呈现,那么嵌入的JS又是如何造成阻塞的呢?通过以下两个代码示例进行对比分析。


代码1:


代码如下:

<ul>
<<li>blogjava></li>
<li>CSDN></li>
<li>博客园></li>
<li>ABC></li>
<li>AAA></li>
<ul>
<div>
<script type="text/javascript">
// 循环5秒钟
var n = Number(new Date());
var n2 = Number(new Date());
while((n2 - n) (6*1000)){
n2 = Number(new Date());
} 
</script>
<div>
<ul>
<li>MSN></li>
<li>GOOGLE></li>
<li>YAHOO></li>
</ul>
</div>

代码2(test.zip里面的代码与代码1的JS代码一模一样):


代码如下:

 
<div>
<ul>
<li>blogjava></li>
<li>CSDN></li>
<li>博客园></li>
<li>ABC></li>
<li>AAA></li>
<ul>
<div>
<script type="text/javascript" src="test.zip"><script>
<div>
<ul>
<li>MSN></li>
<li>GOOGLE></li>
<li>YAHOO></li>
</ul>
</div>

运行后发现,在代码1中,前5秒内页面显示为空白,5秒后页面内容全部显示。而在代码2中,前5秒内blogjava、csdn等内容先显示出来,5秒后MSN才显示出来。


由此可见,嵌入JS会阻塞所有后续内容的呈现,而外部JS仅阻塞其后续内容的显示,但两种方式都会阻塞其后资源的下载。



嵌入JS对CSS加载的影响

嵌入JS导致CSS阻塞加载的问题



CSS通常可以并行下载,但在什么情况下会出现阻塞加载呢?(在测试观察中,IE6下CSS都是阻塞加载,下面的测试在非IE6下进行):


代码1(为了效果,这里选择了一个国外服务器的CSS):


代码如下:

 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>js test>title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link type="text/css" rel="stylesheet" href="http://69.64.92.205/Css/Home3.css" />
</head>
<body>
<img src="https://www.jb51.net/images/logo.gif" /><</span>br />
<img src="http://csdnimg.cn/www/images/csdnindex_piclogo.gif" />
</body>
</html>

时间瀑布图:


外部JS的阻塞下载机制

代码2(只加了1个空的嵌入JS):


<head>


<title>js test>title>


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />


<link type="text/css" rel="stylesheet" href="http://69.64.92.205/Css/Home3.css" />


<script type="text/javascript">


function a(){}


</script>


<head>


<body>


<img src="https://www.jb51.net/images/logo.gif" /><</span>br />


<img src="http://csdnimg.cn/www/images/csdnindex_piclogo.gif" />


</body>


时间瀑布图:


外部JS的阻塞下载机制

从时间瀑布图中可以看出,在代码2中,CSS和图片并没有并行下载,而是等待CSS下载完毕后才去并行下载后面的两个图片。当CSS后面跟着嵌入的JS时,该CSS就会出现阻塞后面资源下载的情况。


有人可能会问,这里为什么不说说嵌入的JS阻塞了后面的资源,而是说CSS阻塞了?想想我们现在用的是一个空函数,解析这个空函数1ms就够,而后面两个图片是等CSS下载完1.3s后才开始下载。大家还可以试试把嵌入JS放到CSS前面,就不会出现阻塞的情况了。


根本原因:因为浏览器会维持HTML中CSS和JS的顺序,样式表必须在嵌入的JS执行前先加载、解析完。而嵌入的JS会阻塞后面的资源加载,所以就会出现上面CSS阻塞下载的情况。


嵌入JS的最佳放置位置与优化建议

嵌入JS应该放在什么位置



1、放在底部,虽然放在底部照样会阻塞所有呈现,但不会阻塞资源下载。


2、如果嵌入JS放在head中,请把嵌入JS放在CSS头部。


3、使用defer


4、不要在嵌入的JS中调用运行时间较长的函数,如果一定要用,可以用setTimeout来调用



PS:很多网站喜欢在head中嵌入JS,并且习惯放在CSS后面,比如看到的www.qq.com,当然也有很多网站是把JS放到CSS前面的,比如yahoo,google



[声明] 转载请注明出处:http://www.blogjava.net/BearRui/。 禁止商用!

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章