JavaScript 文件加載與阻塞問(wèn)題之性能優(yōu)化案例詳解
上來(lái)先給一個(gè)問(wèn)題:在書寫html頁(yè)面時(shí),當(dāng)你要從外部引入js文件時(shí),script標(biāo)簽會(huì)放置在哪個(gè)位置呢,放置位置不同對(duì)頁(yè)面加載有影響嗎?
默認(rèn)情況下,瀏覽器是同步加載 JavaScript 腳本:即渲染引擎遇到 script 標(biāo)簽就會(huì)停下來(lái),等到執(zhí)行完腳本,再繼續(xù)向下渲染。如果是外部腳本,還必須加入腳本下載的時(shí)間。
如果腳本體積很大,下載和執(zhí)行的時(shí)間就會(huì)很長(zhǎng),因此造成瀏覽器堵塞,用戶會(huì)感覺(jué)到瀏覽器“卡死”,出現(xiàn)短暫的空白,沒(méi)有任何響應(yīng)。這會(huì)造成很不好的用戶體驗(yàn),解決這個(gè)問(wèn)題有兩種方案:
①. 改變script標(biāo)簽的放置位置。最好將其丟在body標(biāo)簽的最后面,即放在</ body>標(biāo)簽的前面,這種方式不會(huì)影響瀏覽器的DOM渲染,會(huì)讓頁(yè)面處理執(zhí)行完后才去執(zhí)行它
②. 同步轉(zhuǎn)異步。瀏覽器允許腳本異步加載,這種方式可以讓 script 標(biāo)簽繼續(xù)放在head頭部,下面是兩種異步加載的語(yǔ)法:
<script src="./1.js" async></script> <script src="./1.js" defer></script>
async與defer
異步就是 script 標(biāo)簽打開defer或async屬性,腳本就會(huì)異步加載。瀏覽器渲染引擎遇到這一行命令,就會(huì)開始下載外部腳本,在下載的同時(shí)渲染引擎會(huì)直接執(zhí)行后面的命令。
async和defer都會(huì)讓外部腳本下載時(shí),渲染引擎不停下來(lái)。
async屬性與defer的區(qū)別就在于:
藍(lán)色線代表網(wǎng)絡(luò)讀?。_本下載),紅色線代表執(zhí)行,這倆都是針對(duì)腳本的;綠色線代表 HTML 解析。
defer屬性,瀏覽器會(huì)立即下載相應(yīng)的腳本,在下載的過(guò)程中頁(yè)面的處理不會(huì)停止,等到文檔解析完成后腳本才會(huì)執(zhí)行。
async屬性,瀏覽器會(huì)立即下載相應(yīng)的腳本,在下載的過(guò)程中頁(yè)面的處理不會(huì)停止,下載完成后立即執(zhí)行,執(zhí)行過(guò)程中頁(yè)面處理會(huì)停止。
若不設(shè)置任何屬性,那么當(dāng)與到script腳本時(shí),會(huì)等script腳本下載和執(zhí)行都完成之后才繼續(xù)執(zhí)行下面的頁(yè)面處理。
【注】且async比defer更“厲害”,當(dāng)同一標(biāo)簽同時(shí)使用兩種屬性時(shí),遵循async?。。?/p>
多個(gè)腳本
async和defer的區(qū)別不僅體現(xiàn)在外部script文件的下載和執(zhí)行上,更體現(xiàn)在多個(gè)腳本存在時(shí)的不同:
先來(lái)個(gè)代碼示例:
外部script文件
1.js 文件:
// ... 非常多的js代碼 console.log('1');
2.js 文件:
console.log('2');
主html文件
使用defer:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>js阻塞</title> <!-- defer會(huì)讓dom先執(zhí)行 --> <script src="./1.js" defer></script> <script src="./2.js" defer></script> </head> <body> <h1>js的阻塞是如何進(jìn)行的?</h1> <script> document.addEventListener('DOMContentLoaded', function() { console.log('DOMContentLoaded'); }) </script> </body> </html>
控制臺(tái)執(zhí)行結(jié)果:
使用async:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>js阻塞</title> <!-- defer會(huì)讓dom先執(zhí)行 --> <script src="./1.js" async></script> <script src="./2.js" async></script> </head> <body> <h1>js的阻塞是如何進(jìn)行的?</h1> <script> document.addEventListener('DOMContentLoaded', function() { console.log('DOMContentLoaded'); }) </script> </body> </html>
控制臺(tái)執(zhí)行結(jié)果:
從控制臺(tái)的運(yùn)行結(jié)果就可以看出:
defer:第一個(gè)延遲腳本會(huì)先于第二個(gè)延遲腳本執(zhí)行(當(dāng)然,如果一些瀏覽器不完全遵照html5標(biāo)準(zhǔn),也會(huì)出現(xiàn)不按順序執(zhí)行的情況),且這兩個(gè)腳本會(huì)先于DOMContentLoaded事件執(zhí)行。
async:哪個(gè)先下載完成哪個(gè)就立即執(zhí)行?。。∵@兩個(gè)腳本不一定會(huì)在DOMContentLoaded事件觸發(fā)之前執(zhí)行,但一定會(huì)在window.onload 事件之前執(zhí)行完成。另外,值得注意的是,在先下載完的腳本執(zhí)行過(guò)程中,其他腳本不會(huì)停止下載,而會(huì)繼續(xù)下載。
【注】DOMContentLoaded會(huì)在dom加載完成后觸發(fā),即文檔完全加載和解析之后觸發(fā)。
○ 更多關(guān)于DOMContentLoaded,請(qǐng)參考 http://www.dbjr.com.cn/article/222345.htm
小結(jié)
- 防堵塞的最佳實(shí)踐就是將script標(biāo)簽放到body的最下面;
- defer 和 async 在網(wǎng)絡(luò)讀?。ㄏ螺d)方面是一樣的,都是異步的(相較于 HTML 解析)
它倆的差別在于腳本下載完之后何時(shí)執(zhí)行,顯然 defer 是最接近我們對(duì)于應(yīng)用腳本加載和執(zhí)行的要求的,尤其對(duì)于有script文件之間有依賴的情況(依賴就是可能這個(gè)js文件引用了上一個(gè)js文件的內(nèi)容),它是按照加載順序執(zhí)行腳本的!??! - async 則是一個(gè)亂序執(zhí)行的主,對(duì)它來(lái)說(shuō)腳本的加載和執(zhí)行是緊緊挨著的,所以不管你聲明的順序如何,只要它加載完了就會(huì)立刻執(zhí)行
仔細(xì)想想,async 對(duì)于應(yīng)用腳本的用處不大,因?yàn)樗耆豢紤]依賴(哪怕是最低級(jí)的順序執(zhí)行),不過(guò)它對(duì)于那些可以不依賴任何腳本或不被任何腳本依賴的腳本來(lái)說(shuō)卻是非常合適的,最典型的例子:Google Analytics - defer/async都只適合與操作外部腳本,另外,在操作DOM腳本時(shí)最好不要使用async/defer,因?yàn)楸热缡褂胊sync時(shí)可能頁(yè)面還沒(méi)加載完,就執(zhí)行了js代碼,這樣很可能產(chǎn)生異常;如果一定要使用,可以把需要操作 DOM 的js 部分放在 DOMContentLoaded 事件回調(diào)中執(zhí)行☺!
參考
[1] https://blog.csdn.net/mx18519142864/article/details/82021754
[2] https://blog.csdn.net/weixin_42561383/article/details/86564715
[3] https://segmentfault.com/q/1010000000640869
到此這篇關(guān)于JavaScript 文件加載與阻塞問(wèn)題之性能優(yōu)化案例詳解的文章就介紹到這了,更多相關(guān)JavaScript 文件加載與阻塞問(wèn)題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jQuery判斷密碼強(qiáng)度實(shí)現(xiàn)思路及代碼
實(shí)現(xiàn)思路為:密碼小于六位的時(shí)候,密碼強(qiáng)度圖片都為灰色;密碼為七位及以上并且字母、數(shù)字、特殊字符三項(xiàng)中有兩項(xiàng),強(qiáng)度等等,感興趣的朋友可以參考下哈2013-04-04JavaScript設(shè)計(jì)模式之模板方法模式原理與用法示例
這篇文章主要介紹了JavaScript設(shè)計(jì)模式之模板方法模式原理與用法,結(jié)合實(shí)例形式分析了JavaScript模板方法模式的概念、組成、定義、使用等相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-08-08javascript string字符串優(yōu)化問(wèn)題
今天看到一個(gè)很久的帖子說(shuō)string連接優(yōu)化問(wèn)題。于是自己也測(cè)試一下。寫了個(gè)很簡(jiǎn)單的代碼2011-07-07uniapp?u-picker多列用法以及設(shè)置默認(rèn)選中值
uview組件庫(kù)u-picker組件可能很多人不熟悉,下面這篇文章主要給大家介紹了關(guān)于uniapp?u-picker多列用法以及設(shè)置默認(rèn)選中值的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-06-06js一維數(shù)組、多維數(shù)組和對(duì)象的混合使用方法
這篇文章主要介紹了js一維數(shù)組、多維數(shù)組和對(duì)象的混合使用方法,需要的朋友可以參考下2016-04-04JS動(dòng)態(tài)添加元素及綁定事件造成程序重復(fù)執(zhí)行解決
這篇文章主要給大家介紹了關(guān)于JS動(dòng)態(tài)添加元素及綁定事件造成程序重復(fù)執(zhí)行的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-12-12js實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)非select下拉框版
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)非select下拉框版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09js 靜態(tài)動(dòng)態(tài)成員 and 信息的封裝和隱藏
一下用面向?qū)ο蟮南嚓P(guān)概念來(lái)解釋js中的仿面向?qū)ο?,因?yàn)閖s中不像其他語(yǔ)言,不存在面向?qū)ο笳Z(yǔ)言的相關(guān)特性2011-05-05