如何减少JS阻塞特性造成的困扰
来源:互联网 发布:elk 大数据平台 编辑:程序博客网 时间:2024/05/22 06:26
JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即<script>每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面。
由于,JS的这种阻塞特性,每次遇到<script>,页面都必须停下来等待脚本下载并执行,这会停止页面绘制,带来不好的用户体验。所以,有必要减少JS阻塞特性造成的困扰。
1 优化脚本位置
HTML4规范中,<script>可以放在<head>或<body>中。你可能习惯性的在<head>中放置多个外链JS、CSS,以求优先加载它们。浏览器在继续到<body>之前,不会渲染页面,所以,把JS放在<head>中,会导致延迟。为了提高用户体验,新一代浏览器都支持并行下载JS,但是JS下载仍然会阻塞其它资源的下载(eg.图片)。尽管脚本的下载过程并不会相互影响,但页面仍然必须等待所有JS下载并执行完成才能继续。显见,所有<script>应该尽可能放到<body>的底部,以减少对页面下载的影响。
注意:CSS文件本身是并行下载,不会阻塞页面的其他进程。但是,如果把一段内嵌脚本放在引用外链CSS的<link>之后会导致页面阻塞去等待CSS的下载。这样做是为了确保内嵌脚本在执行时能够获得正确的样式信息。所以,最好不要把内嵌脚本放在CSS的<link>之后。
2 减少外链脚本数量以改善性能
原因很简单,额外的HTTP请求会带来额外的开销,所以减少页面中外链脚本的数量,有助于改善性能。
3 使用无阻塞下载JS方法
无阻塞脚本的秘诀在于,在页面加载完成后才加载JS,即在window对象的load事件触发后在下载脚本。
3.1 使用<script>的defer属性(仅IE和Firefox3.5以上);
defer属性指明本元素所含的脚本不会修改DOM,因此代码能安全的延迟执行。defer属性的<script>,对应的JS文件将在页面解析到<script>时开始下载,但并不会执行,直到DOM加载完成,即onload事件触发前被调用。当一个带有defer属性的JS文件下载时,他不会阻塞浏览器的其它进程,因此这类文件可以与页面中的其他资源并行下载。
<!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> <title>DeferredScripts</title></head><body> <script type="text/javascript" defer> alert("defer"); </script> <script type="text/javascript"> alert("script"); </script> <script type="text/javascript"> window.onload = function() { alert("load"); }; </script></body></html>
对于支持defer的浏览器弹出顺序是:script>defer>load;而不支持该属性的浏览器的弹出顺序为:defer>script>load。
3.2 使用动态创建的<script>元素来下载并执行代码
实例代码如下:
<!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> <title>DynamicScriptElements</title></head><body> <script type="text/javascript"> function loadScript(url, callback) { var script = document.createElement("Script"); script.type = "text/javascript"; //IE 验证脚本是否下载完成 if (script.readyState) { script.onreadystatechange = function() { //readyState属性有5种取值 //uninitialized:初始状态 //loading:开始下载 //interactive:数据完成下载但尚不可用 //complete:数据已经准备就绪 //实际使用时,readyState的值并不像我们预想的那样有规律,实践发现使用readyState //最靠谱的方式是同时检查以下2个状态,只要其中1个触发,就认为脚本下载完成。 if (script.readyState == "loaded" || script.readyState == "complete") { //移除事件处理器,确保事件不会处理2次 script.onreadystatechange = null; callback(); } }; } //其他浏览器 else { script.onload = function() { callback(); }; } script.src = url; //把新建的<Script>添加到<head>里比添加到<body>里更保险。 document.getElementsByTagName("head")[0].appendChild(script); } //动态加载多个JS文件 //优先加载Common.js,等待Common.js加载完毕后加载Costom.js //不同浏览器的执行顺序不同 //Firefox、Opera能够保证按照你脚本的加载顺序来执行 //其他浏览器会按照从服务端返回的顺序执行代码,因此使用嵌套的方法保证调用顺序 loadScript("Common.js", function() { loadScript("Costom.js", function() { alert("all load"); }); }); </script></body></html>
文件在该元素被添加到页面时开始下载。这种技术的重点在于:无论在何时启动下载,文件的下载与执行不会阻塞页面的其他进程。使用动态脚本节点下载文件时,根据浏览器不同,多数浏览器,返回的代码会立即执行(Firefox、Opera,会等待此前所有动态节点执行完毕)。当脚本”自执行“时,这种机制运行正常,但是当代码内只包含供其它脚本调用的接口时,就必须确保脚本下载完成并准备就绪,在上例中列举了不同浏览器的验证方法。
注意:如果多个文件的顺序很重要,更好的做法是把它们按正确顺序合并为一个文件。此外,说把新建的<Script>添加到<head>里比添加到<body>里更保险是因为要尽量避免页面报错(在低版本的IE中使用不当会发生"操作已中止"错误。
3.3 使用XHR对象下载JS代码并注入页面中
实例代码如下:
<!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> <title>XhrScriptInjection</title></head><body> <script type="text/javascript"> var xhr = new XMLHttpRequest(); xhr.open("get", "JScript.js", true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { //2XX表示有效响应,304表示从缓存读取 if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) { //创建内嵌脚本 var script = document.createElement("script"); script.type = "text/javascript"; script.text = xhr.responseText; document.body.appendChild(script); //一旦新创建的<script>被添加到页面,代码就立刻执行然后准备就绪。 } } }; xhr.send(null); </script></body></html>
这种方法的优点是,你可以下载JS代码但不立即执行。由于代码是在<script>标签之外返回的,因此它下载后不会自动执行,这使得你可以把脚本的执行推迟到你准备好的时候。另一个优点是,同样的代码在所有主流浏览器中都能正常工作。
这种方法的主要局限性是JS文件必须与所有请求的页面处于相同的域。
综上所述,向页面中添加大量JS的推荐做法只需两步:先添加动态加载的所需代码,然后加载初始化页面所需的剩下代码。
<script type="text/javascript" src="Common.js"></script> <script type="text/javascript"> loadScript("Costom.js", function() { //Do Something }); </script>
优化前:
优化后:
"操作已中止"错误<html><head> <title>Operation Aborted Example</title></head><body> <p>The following code should cause an Operation Aborted error in IE versions prior to 8.</p> <div> <script type="text/javascript"> document.body.appendChild(document.createElement("div")); </script> </div></body></html>上述代码在低版本IE中会报"操作已中止"错误。出现此问题的原因是子容器 HTML 元素包含试图修改父容器元素的子容器的脚本。脚本试图使用 innerHTML 方法或 appendChild 方法修改父容器元素。例如对于如果 DIV 元素是一个 BODY 元素中的子容器,并且在 DIV 元素中的一个 SCRIPT 块试图修改 DIV 元素的父容器的 BODY 元素可能会出现此问题。
最简单的解决方法:将脚本移到body元素的范围。
<html><head> <title>Operation Aborted Example</title></head><body> <p>The following code should cause an Operation Aborted error in IE versions prior to 8.</p> <div> </div> <script type="text/javascript"> document.body.appendChild(document.createElement("div")); </script></body></html>
- 如何减少JS阻塞特性造成的困扰
- ESD对电子设备造成的困扰
- nodejs异步回调造成的困扰
- js阻塞特性
- 如何减少频繁分配内存(malloc或者new)造成的内存碎片?
- 如何减少频繁分配内存(malloc或者new)造成的内存碎片?
- 如何减少频繁分配内存(malloc或者new)造成的内存碎片?
- Mysql的float类型造成的困扰总结
- Swift的类型推断会不会造成类型不确定的困扰?
- Process的的waitFor()造成的阻塞
- Process的的waitFor()造成的阻塞
- Process 的 waitFor() 造成的阻塞
- JS学习笔记(一)——JS的阻塞特性
- js 减少if的使用
- uclinux-2008R1-RC8(bf561)到VDSP5的移植(41):PLL造成的困扰
- 如何减少可执行文件的代码
- 如何减少代码的量
- 如何减少App的大小
- 指向数组的指针&数组名和指针的区别
- 基站定位查询api使用接口
- Android 使用Aquery加载图片
- 流程管理在HSE中应用
- 关于户口和档案
- 如何减少JS阻塞特性造成的困扰
- 数据结构之冒泡排序的Java实现
- Unity3D旋转无效
- 第二章:线性表
- hao123流氓设置浏览器注意并且导致蓝屏的问题
- C语言错误提示集锦
- hdu 5000 Clone 2014 ACM/ICPC Asia Regional Anshan Online
- nyoj 1057 寻找最大数(三) 贪心
- 处女男学Android(五)---Handler完结篇之Handler的post方法详解