摘要:現在對的使用非常普遍,任何一個站點都會請求大量的腳本,而加載和執行的方式也是各不相同,希望讀完這篇文章可以對常用的加載和執行方式有一個整體的認識。總結上文主要介紹了動態創建腳本和的方式去創建異步加載和執行腳本的方式。
在打開一個站點的時候,瀏覽器會去加載各種資源。現在對JS的使用非常普遍,任何一個站點都會請求大量的JS腳本,而加載和執行的方式也是各不相同,希望讀完這篇文章可以對常用的加載和執行方式有一個整體的認識。
首先介紹的是html中直接使用標簽。這也是我們最常見的一種加載腳本的方式。
// 直接使用script內嵌腳本// 使用script的src屬性引用外部腳本
這種方式在主流的瀏覽器可以是并行加載的,但是執行腳本的順序還是同步的,再加上瀏覽器是順序解析頁面,所以對于腳本的位置是有一定講究的:
如果腳本B需要使用到腳本A中的數據(比如fn或者變量),那腳本B必須放在腳本A后面。
因為執行引擎是單線程的,所以在執行JS的時候會阻塞DOM的渲染,導致頁面長時間空白。如果不想的話可以考慮把JS放在文檔的后面(比如body標簽的底部)。
一個外鏈腳本就涉及到一個請求,再小的請求肯定都會有性能開銷,比如請求頭,網絡時延等等。所以在優化站點性能的時候減少外鏈也是要考慮的一個點。
【注】:可并行加載的瀏覽器包括:IE8+、firefox3.5+、safari4+和chrome2+。不同瀏覽器對于同一個域名下的最大連接數有不同的限制,基本在6個左右。具體可以參見這篇文章。
在程序的世界中,很多場景下同步阻塞就意味著性能問題。在這些場景下其實無阻塞腳本就可以搞定,主進程還是去干當前最主要的事情,即加載和渲染DOM,而無阻塞的腳本可以等到頁面加載完再去加載執行,這些場景正是性能優化的點。
這就引申出來了幾種無阻塞腳本的方案。
方案一:defer屬性 【w3c】【MDN】這個屬性的是承諾用src引的腳本中不會修改DOM。放心的讓這個腳本延遲執行吧。具體延遲到文檔完成解析后,觸發 DOMContentLoaded 事件前執行。
【注1】執行是被延遲了,但是下載還是根據script在頁面中的位置。解析到時會去并行下載,但是不會執行。
【注2】由上述定義可以看出來需由src的存在,對于內嵌的腳本是無效的。
【注3】配了defer屬性的腳本之間是按照順序執行的【測試】 chrome 64.0.3282.119
// demo.js console.log("inner demo.js");
結果:
方案三:動態腳本配置了async屬性是告訴瀏覽器,這個腳本異步去并行加載,加載完立即異步執行,但是加載的時機是不確定的,所以這個屬性比defer更開放。相關測試代碼
【注1】因為異步加載完就立即異步執行,所以配了這個屬性的腳本之間的關系也是不確定的。所以不能存在依賴async腳本內容的情況。
【注2】執行的時機智能確定在load事件之前,和DOMContentLoaded的時機不能確定
【注3】優先級是高于defer的
【注4】和defer一樣,對內嵌腳本無效;不能有document.write改寫dom的代碼
動態腳本是我們比較常用的異步加載和執行JS的方式。這種實現要特別注意瀏覽器的兼容性。簡單的實現方式如下:
function loadJs(url,callback) { var callback = callback || (() => {}); var script = document.createElement("script"); script.type = "text/javascript"; script.src = url; if(script.readyState){ //IE script.onreadystatechange = function () { if(script.readyState == "loaded" || script.readyState == "complete"){ console.log("inner onreadystatechange"); script.onreadystatechange = null; callback(); } }; } else { script.onload = function () { console.log("inner onload"); callback(); }; } document.getElementsByTagName("head")[0].appendChild(script); // 開始下載并執行 } loadJs("./server.js");
這種創建的方式,文件在該元素被添加到頁面時開始下載,加載完開始執行,并且文件的下載和執行過程不會阻塞其它進程。可以認為這種創建默認加了async屬性。我們還可以通過設置async = false的方式取消異步的特性。正因為這個特性,絕大多數場景下都是有益的,但是當我們想使用這種方式去加載多個JS時,并且有先后順序的時候,可以嘗試在callbak里去迭代發請求。
loadJs("f1.js",()=>{ loadJs("f2.js",()=>{ loadJs(xxx); }) });方案四:XMLHttpRequest(XHR)注入腳本
搞過Ajax的對XHR應該都很熟悉了,在這就不詳細介紹了,需要的去Google一把。
XHR主要是請求腳本,然后我們可以控制請求回來的腳本,在我們需要的時候通過上述動態創建腳本的方式注入到頁面中。這種方式最大的好處就是兼容性好。弊端也很明顯,必須同源。下面是一個簡單實現,沒有考慮在創建xhr的兼容性,比如ActiveXObject,有需要的可以去google一把:
var xhr = new XMLHttpRequest(); // 創建xhr對象 xhr.open("get","./server.js",true); // 初始化一個請求, 支持CRUD xhr.onreadystatechange = function () { if(xhr.readyState == 4){ if(xhr.status >= 200 && xhr.status < 3000 || xhr.status == 304){ var script = document.createElement("script"); script.type = "text/javascript"; script.text = xhr.responseText; document.body.appendChild(script); } } } xhr.send(null); // 發送請求
這地方說明一下xhr.status:
值 | 狀態 | 描述 |
---|---|---|
0 | UNSENT (未打開) | open()方法還未被調用. |
1 | OPENED (未發送) | open()方法已經被調用. |
2 | HEADERS_RECEIVED (已獲取響應頭) | send()方法已經被調用, 響應頭和響應狀態已經返回 |
3 | LOADING (正在下載響應體) | 響應體下載中; responseText中已經獲取了部分數據. |
4 | DONE (請求完成) | 整個請求過程已經完畢. |
對于document.write,一般都不推薦使用的,主要是因為存在write方法的腳本可能會在解析的過程中修改DOM,導致一些腳本無法預加載,甚至會導致一些已經預解析和預加載失效。網上也很多關于為什么要避免使用document.write的文章,感興趣的可以去google一把。
innerHtml對于innerHtml和outerHTML, 只會以字符串的形式來承載,不會去執行對應的腳本的。
總結:上文主要介紹了動態創建腳本和XHR的方式去創建異步加載和執行腳本的方式。在某些性能調優的情況下還是很有用的,而XHR更是Ajax的核心。
參考高性能Javascript
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107416.html
摘要:下面介紹非阻塞加載腳本技術也就是異步加載。非阻塞加載腳本關于的一篇好文目前所有瀏覽器都支持屬性,但是和中只有在加載外部腳本時才會生效,行內腳本使用是沒有作用的。在中,只有外部腳本才會發生阻塞。 上篇博客說過腳本后置可以使頁面更快的加載,可是這樣的優化還是有限的,如果腳本需要執行一個耗時的操作,就算后置了它還是會阻塞后續腳本加載和執行并且阻塞整個頁面。下面介紹非阻塞加載腳本技術也就是...
摘要:所以有可能在所有腳本執行完畢后觸發。如果用戶即將離開頁面或者關閉窗口時,事件將會被觸發以進行額外的確認。狀態表示事件即將被觸發。總結頁面事件的生命周期事件在樹構建完畢后被觸發,我們可以在這個階段使用去訪問元素。 頁面生命周期:DOMContentLoaded, load, beforeunload, unload 原文地址:http://javascript.info/onload.....
摘要:所以有可能在所有腳本執行完畢后觸發。如果用戶即將離開頁面或者關閉窗口時,事件將會被觸發以進行額外的確認。狀態表示事件即將被觸發。總結頁面事件的生命周期事件在樹構建完畢后被觸發,我們可以在這個階段使用去訪問元素。 頁面生命周期:DOMContentLoaded, load, beforeunload, unload 原文地址:http://javascript.info/onload.....
閱讀 3196·2021-09-06 15:02
閱讀 2242·2019-08-30 15:48
閱讀 3438·2019-08-29 11:08
閱讀 3280·2019-08-26 13:55
閱讀 2439·2019-08-26 13:35
閱讀 3161·2019-08-26 12:11
閱讀 2597·2019-08-26 11:48
閱讀 881·2019-08-26 11:42