頁面中引入的JS文件是阻塞式加載的,這樣會影響頁面性能。以下是JS文件性能優化方法:
一:將所有的<script>標簽放到頁面底部,也就是</body>閉合標簽之前,這能確保在腳本執行前頁面完成了渲染
由於JS文件是阻塞式加載,當加載JS文件時,頁面停止渲染,這樣頁面會出現不完整狀態。將JS文件的<script>放在頁面底部,這樣能在加載JS文件時確保頁面渲染完成。
二:盡可能的合並腳本文件。頁面中<script>標簽越少,加載也就越快,響應速度就越快。無論是外鏈腳本還是內嵌腳本都是如此。
通常一個大型網站或應用需要依賴多個JS文件。可以把多個文件合並成一個,這樣只需要引用一個<script>標簽,就可以減少性能消耗。文件合並的工作可通過離線的打包工具或者一些實時的
在線服務來實現。
注意:內嵌腳本放在引用的外鏈樣式表的<link>之后會導致阻塞去等待樣式表單下載。這樣做事為了確保內嵌腳本在執行時能獲得最精確的樣式信息。因此,建議不要把內嵌腳本緊跟在<link>標簽后面
三:采用無阻塞下載JS腳本的方法。
較少JS文件大小並限制HTTP請求數在功能豐富的WEB應用或大型網站上並不是總可行。WEB應用的功能越豐富,所需要的JS代碼就越多,盡管下載單個較大的JS文件只產生一個HTTP請求,卻會鎖死瀏覽器的一大段時間。為避免這種情況,需要通過一些特定的技術向頁面中逐步加載JS文件,這樣做在某種程度上說不會阻塞瀏覽器。
無阻塞加載腳本的本質:在頁面加載完成后才加載JS代碼。這就意味着在window.onload事件觸發后再下載腳本。實現方法:
1.延遲加載腳本:
HTML4為<script>標簽定義了一個擴展屬性:defer。但defer屬性只被IE4和FireFox3.5更高版本的瀏覽器支持。所以不是一個跨瀏覽器解決方案。在其他瀏覽器中,defer直接被忽略,JS文件仍會造成阻塞。如果瀏覽器支持defer屬性,可以這樣寫:
1 <script type="text/javascript" src="script1.js" defer></script>
帶有defer屬性的<script>標簽可以放置在文檔的任何位置。對應的JS文件將在頁面解析到<script>標簽時開始下載,但不會執行。直到DOM加載完成,即onload事件觸發前才會執行。
當帶有defer屬性的JS文件下載時,不會阻塞瀏覽器的其他進程,因此這類文件可以與其他資源文件一起下載。
1 <html> 2 <head> 3 <title>Script Defer Example</title> 4 </head> 5 <body> 6 <script type="text/javascript" defer> 7 alert("defer"); 8 </script> 9 <script type="text/javascript"> 10 alert("script"); 11 </script> 12 <script type="text/javascript"> 13 window.onload = function(){ 14 alert("load"); 15 }; 16 </script> 17 </body> 18 </html>
不支持 defer
屬性的瀏覽器的彈出順序是:“defer”、“script”、“load”。而在支持 defer
屬性的瀏覽器上,彈出的順序則是:“script”、“defer”、“load”。請注意,帶有 defer
屬性的<script>
元素不是跟在第二個后面執行,而是在 onload
事件被觸發前被調用。
2.異步加載JS文件:async屬性:使用所有瀏覽器
HTML5為<script>標簽定義了一個新的擴展屬性:async。作用和defer一樣,能夠異步加載和執行腳本,不因為加載腳本而阻塞頁面渲染。但是:使用async,JS腳本一旦下載好了就好執行,所以很有可能不是按照原本的順序來執行。如果JS腳本前后有依賴性,使用async和有可能會出錯。
1 <script type="text/javascript" src="script1.js" async="true"></script>
四:動態腳本元素
文檔對象模型(DOM)允許您使用 JavaScript 動態創建 <script>
元素。
1 var script = document.createElement ("script"); 2 script.type = "text/javascript"; 3 script.src = "script1.js"; 4 document.getElementsByTagName("head")[0].appendChild(script);
新的<script>元素加載script1.js源文件。此文件當元素添加到頁面之后會立刻開始下載。技術重點:無論在何處啟動下載,文件的下載和運行都不會阻塞其他頁面處理過程。可以將這些代碼放在<head>部分而不會對其余部分的頁面代碼造成影響(除了用於下載文件的HTTP連接)。
當文件使用動態腳本節點下載時,返回的代碼通常是立即執行(除了FireFox和Opera,他們講等待此前的所有動態腳本執行完畢)。當腳本是“自運行”類型時,這一機制運行正常。但是如果腳本只包含供頁面其他腳本調用的接口,則會帶來問題。這種情況下,您需要跟蹤腳本下載完成是否准備妥善,可以使用動態<script>節點發出事件得到相關信息。
新的<script>
元素加載 script1.js 源文件。此文件當元素添加到頁面之后立刻開始下載。此技術的重點在於:無論在何處啟動下載,文件的下載和運行都不會阻塞其他頁面處理過程。您甚至可以將這些代碼放在<head>
部分而不會對其余部分的頁面代碼造成影響(除了用於下載文件的 HTTP 連接)。
Firefox、Opera, Chorme 和 Safari 3+會在<script>
節點接收完成之后發出一個 onload
事件。您可以監聽這一事件,以得到腳本准備好的通知:
1 var script = document.createElement ("script") 2 script.type = "text/javascript"; 3 4 //Firefox, Opera, Chrome, Safari 3+ 5 script.onload = function(){ 6 alert("Script loaded!"); 7 }; 8 9 script.src = "script1.js"; 10 document.getElementsByTagName("head")[0].appendChild(script);
Internet Explorer 支持另一種實現方式,它發出一個 readystatechange
事件。<script>
元素有一個 readyState
屬性,它的值隨着下載外部文件的過程而改變。readyState
有五種取值:
- “uninitialized”:默認狀態
- “loading”:下載開始
- “loaded”:下載完成
- “interactive”:下載完成但尚不可用
- “complete”:所有數據已經准備好
微軟文檔上說,在<script>
元素的生命周期中,readyState
的這些取值不一定全部出現,但並沒有指出哪些取值總會被用到。實踐中,我們最感興趣的是“loaded”和“complete”狀態。Internet Explorer 對這兩個 readyState
值所表示的最終狀態並不一致,有時<script>
元素會得到“loader”卻從不出現“complete”,但另外一些情況下出現“complete”而用不到“loaded”。最安全的辦法就是在 readystatechange
事件中檢查這兩種狀態,並且當其中一種狀態出現時,刪除 readystatechange
事件句柄(保證事件不會被處理兩次):
1 var script = document.createElement("script") 2 script.type = "text/javascript"; 3 4 //Internet Explorer 5 script.onreadystatechange = function(){ 6 if (script.readyState == "loaded" || script.readyState == "complete"){ 7 script.onreadystatechange = null; 8 alert("Script loaded."); 9 } 10 }; 11 12 script.src = "script1.js"; 13 document.getElementsByTagName("head")[0].appendChild(script);
注意:大多數情況下,您希望調用一個函數就可以實現 JavaScript 文件的動態加載。下面的函數封裝了標准實現和 IE 實現所需的功能:通過函數進行封裝
1 function loadScript(url, callback){ 2 var script = document.createElement ("script") 3 script.type = "text/javascript"; 4 if (script.readyState){ //IE 5 script.onreadystatechange = function(){ 6 if (script.readyState == "loaded" || script.readyState == "complete"){ 7 script.onreadystatechange = null; 8 callback(); 9 } 10 }; 11 } else { //Others 12 script.onload = function(){ 13 callback(); 14 }; 15 } 16 script.src = url; 17 document.getElementsByTagName("head")[0].appendChild(script); 18 }
loadScript("script1.js", function(){ alert("File is loaded!"); });
在頁面中動態加載很多 JavaScript 文件,但要注意,瀏覽器不保證文件加載的順序。所有主流瀏覽器之中,只有 Firefox 和 Opera 保證腳本按照您指定的順序執行。其他瀏覽器將按照服務器返回它們的次序下載並運行不同的代碼文件。但是可以將下載操作串聯在一起以保證他們的次序,如下:
1 loadScript("script1.js", function(){ 2 loadScript("script2.js", function(){ 3 loadScript("script3.js", function(){ 4 alert("All files are loaded!"); 5 }); 6 }); 7 });
此代碼等待 script1.js 可用之后才開始加載 script2.js,等 script2.js 可用之后才開始加載 script3.js。雖然此方法可行,但如果要下載和執行的文件很多,還是有些麻煩。如果多個文件的次序十分重要,更好的辦法是將這些文件按照正確的次序連接成一個文件。獨立文件可以一次性下載所有代碼(由於這是異步進行的,使用一個大文件並沒有什么損失)。
動態腳本加載是非阻塞 JavaScript 下載中最常用的模式,因為它可以跨瀏覽器,而且簡單易用。
五:使用 XMLHttpRequest(XHR)對象
此技術首先創建一個 XHR 對象,然后下載 JavaScript 文件,接着用一個動態 <script>
元素將 JavaScript 代碼注入頁面。
1 var xhr = new XMLHttpRequest(); 2 xhr.open("get", "script1.js", true); 3 xhr.onreadystatechange = function(){ 4 if (xhr.readyState == 4){ 5 if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ 6 var script = document.createElement ("script"); 7 script.type = "text/javascript"; 8 script.text = xhr.responseText; 9 document.body.appendChild(script); 10 } 11 } 12 }; 13 xhr.send(null);
方法的主要優點是,可以下載不立即執行的 JavaScript 代碼。由於代碼返回在<script>
標簽之外(換句話說不受<script>
標簽約束),它下載后不會自動執行,這使得您可以推遲執行,直到一切都准備好了。另一個優點是,同樣的代碼在所有現代瀏覽器中都不會引發異常。