前端性能优化成神之路-异步加载
浏览器渲染阻塞与优化
异步加载的方式1:动态脚本加载
JavaScript 倾向于阻塞浏览器某些处理过程,如 HTTP 请求和界面刷新,这是开发者面临的最显著的性能问题。保持 JavaScript 文件短小,并限制 HTTP 请求的数量,只是创建反应迅速的网页应用的第一步。一个应用程序所包含的功能越多,所需要的 JavaScript 代码就越大,保持源码短小并不总是一种选择。尽管下载一个大 JavaScript 文件只产生一次 HTTP 请求,却会锁定浏览器一大段时间。为避开这种情况,你需要向页面中逐步添加 JavaScript,某种程度上说不会阻塞浏览器。
var script = document.createElement ("script"); script.type = "text/javascript"; script.src = "file1.js"; document.getElementsByTagName_r("head")[0].appendChild(script);
然后再页面中通过动态创建script标签的方式加载这个js文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class=""> test <script type="text/javascript"> console.log('write'); document.write('<span>write</span>'); </script> <script type="text/javascript"> for (var i = 0; i < 200000; i++) { if (i % 20000 === 0) { console.log(i); } } </script> <script> function loadScript(url, callback){ var script = document.createElement("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName("head")[0].appendChild(script); } loadScript("file1.js", function(){ alert("File is loaded!"); }); </script> </div> </body> </html>
loadScript("file1.js", function(){ loadScript("file2.js", function(){ loadScript("file3.js", function(){ alert("All files are loaded!"); }); }); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class=""> test <script type="text/javascript"> console.log('write'); document.write('<span>write</span>'); </script> <script type="text/javascript"> for (var i = 0; i < 200000; i++) { if (i % 20000 === 0) { console.log(i); } } </script> <script> var xhr = new XMLHttpRequest(); xhr.open("get", "file1.js", true); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ 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); } } }; xhr.send(null); </script> </div> </body> </html>
执行的效果如下:
异步加载方式3:defer
HTML 4 为script标签定义了一个扩展属性:defer。
defer是在HTML解析完成之后就会执行,如果有多个,按照加载的顺序依次执行
Defer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中,defer 属性会被直接忽略,因此script标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。使用方式如下:
<script type="text/javascript" src="script1.js" defer></script>
带有 defer 属性的script标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到script标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载
实例演示:
创建两个js分别为defer1.js和defer2.js
然后在页面中进行加载
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="defer1.js" charset="utf-8" defer></script> <script src="defer2.js" charset="utf-8" defer></script> </head> <body> <div class=""> test <script type="text/javascript"> console.log('write'); document.write('<span>write</span>'); </script> <script type="text/javascript"> for (var i = 0; i < 200000; i++) { if (i % 20000 === 0) { console.log(i); } } </script> </div> </body> </html>
运行结果:
可以看到页面中两段js代码先执行(同步的方式),后执行了异步加载的两个js文件的代码
异步加载方式4:async
HTML 5 为script标签定义了一个新的扩展属性:async
它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的渲染。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。
演示实例:
创建两个js文件
然后再页面中进行加载
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="async1.js" charset="utf-8" async></script> <script src="async2.js" charset="utf-8" async></script> </head> <body> <div class=""> test <script type="text/javascript"> console.log('write'); document.write('<span>write</span>'); </script> <script type="text/javascript"> for (var i = 0; i < 200000; i++) { if (i % 20000 === 0) { console.log(i); } } </script> </div> </body> </html>
运行结果如下:
当两个js文件的代码量比较大的时候,就有可能出现先执行async2.js这个文件的代码