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