網頁性能優(yōu)化之異步加載js文件

一個網頁的有很多地方可以進行性能優(yōu)化,比較常見的一種方式就是異步加載js腳本文件蛀蜜。在談異步加載之前办斑,先來看看瀏覽器加載js文件的原理。

瀏覽器加載 JavaScript 腳本赖草,主要通過<script>元素完成学少。正常的網頁加載流程是這樣的。

  1. 瀏覽器一邊下載 HTML 網頁疚顷,一邊開始解析旱易。也就是說,不等到下載完腿堤,就開始解析阀坏。
  2. 解析過程中,瀏覽器發(fā)現<script>元素笆檀,就暫停解析忌堂,把網頁渲染的控制權轉交給 JavaScript 引擎。
  3. 如果<script>元素引用了外部腳本酗洒,就下載該腳本再執(zhí)行士修,否則就直接執(zhí)行代碼。
  4. JavaScript 引擎執(zhí)行完畢樱衷,控制權交還渲染引擎棋嘲,恢復解析 HTML 網頁。

加載外部腳本時矩桂,瀏覽器會暫停頁面渲染沸移,等待腳本下載并執(zhí)行完成后,再繼續(xù)渲染侄榴。原因是 JavaScript 代碼可以修改 DOM雹锣,所以必須把控制權讓給它,否則會導致復雜的線程競賽的問題癞蚕。

上面所說的蕊爵,就是我們平時最常見到的,將<script>標簽放到<head>中的做法桦山,這樣的加載方式叫做同步加載攒射,或者叫阻塞加載,因為在加載js腳本文件時度苔,會阻塞瀏覽器解析HTML文檔匆篓,等到下載并執(zhí)行完畢之后,才會接著解析HTML文檔寇窑。如果加載時間過長(比如下載時間太長)鸦概,就會造成瀏覽器“假死”,頁面一片空白。而且窗市,放在<head>中同步加載的js文件中不能對DOM進行操作先慷,否則會產生錯誤,因為這個時候HTML還沒有進行解析咨察,DOM還沒有生成论熙。由此看來,同步加載帶來的體驗往往并不好摄狱。

下面我們來看幾種異步加載的方式脓诡。

  1. <script>標簽放到<body>底部
    嚴格來說,這并不算是異步加載媒役,但是這也是常見的通過改變js加載方式來提升頁面性能的一種方式祝谚,所以也就放到這里來說。
    <script>放到<body>底部酣衷,解決上上面說到的幾個問題交惯,一是不會造成頁面解析的阻塞,就算加載時間過長用戶也可以看到頁面而不是一片空白穿仪,而且這時候可以在腳本中操作DOM席爽。
  2. defer屬性
    通過給<script>標簽設置defer屬性,將腳本文件設置為延遲加載啊片,當瀏覽器遇到帶有defer屬性的<script>標簽時只锻,會再開啟一個線程去下載js文件,同時繼續(xù)解析HTML文檔紫谷,等等HTML全部解析完畢DOM加載完成之后炬藤,再去執(zhí)行加載好的js文件。
    這種方式只適用于引用外部js文件的<script>標簽碴里,可以保證多個js文件的執(zhí)行順序就是它們在頁面中出現的順序,但是要注意上真,添加defer屬性的js文件不應該使用document.write方法咬腋。
  3. async屬性
    async屬性和defer屬性類似,也是會開啟一個線程去下載js文件睡互,但和defer不同的時根竿,它會在下載完成后立刻執(zhí)行,而不是會等到DOM加載完成之后再執(zhí)行就珠,所以還是有可能會造成阻塞寇壳。
    同樣的,async也是只適用于外部js文件妻怎,也不能在js中使用document.write方法壳炎,但是對多個帶有async的js文件,它不能像defer那樣保證按順序執(zhí)行逼侦,它是哪個js文件先下載完就先執(zhí)行哪個匿辩。
  4. 動態(tài)創(chuàng)建<script>標簽
    可以通過動態(tài)地創(chuàng)建<script>標簽來實現異步加載js文件腰耙,例如下面代碼:
    (function(){
        var scriptEle = document.createElement("script");
        scriptEle.type = "text/javasctipt";
        scriptEle.async = true;
        scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
        var x = document.getElementsByTagName("head")[0];
        x.insertBefore(scriptEle, x.firstChild); 
    })();
    
    或者
    (function(){
        if(window.attachEvent){
            window.attachEvent("load", asyncLoad);
        }else{
            window.addEventListener("load", asyncLoad);
        }
        var asyncLoad = function(){
        var ga = document.createElement('script'); 
        ga.type = 'text/javascript'; 
        ga.async = true; 
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 
        var s = document.getElementsByTagName('script')[0]; 
        s.parentNode.insertBefore(ga, s);
        }
    })();
    
    上面兩種方法中,第一種方式執(zhí)行完之前會阻止onload事件的觸發(fā)铲球,而現在很多頁面的代碼都在onload時還執(zhí)行額外的渲染工作挺庞,所以還是會阻塞部分頁面的初始化處理。第二種則不會阻止onload事件的觸發(fā)稼病。
    這里要簡要說明一下window.DOMContentLoadedwindow.onload這兩個事件的區(qū)別选侨,前者是在DOM解析完畢之后觸發(fā),這時候DOM解析完畢然走,JavaScript可以獲取到DOM引用援制,但是頁面中的一些資源比如圖片、視頻等還沒有加載完丰刊,作用同jQuery中的ready事件隘谣。后者則是頁面完全加載完畢,包括各種資源啄巧。

說完了這幾種常見的異步加載js腳本的方式寻歧,再來看最后一個問題,什么時候用defer秩仆,什么時候用async呢码泛?一般來說,兩者之間的選擇則是看腳本之間是否有依賴關系澄耍,有依賴的話應當要保證執(zhí)行順序噪珊,應當使用defer沒有依賴的話使用async,同時使用的話defer失效齐莲。要注意的是兩者都不應該使用document.write痢站,這個導致整個頁面被清除。

下面一幅圖表明了同步加載以及defer选酗、async加載時的區(qū)別阵难,其中綠色線代表 HTML 解析,藍色線代表網絡讀取js腳本芒填,紅色線代表js腳本執(zhí)行時間:

同步加載呜叫、defer、async的區(qū)別

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末殿衰,一起剝皮案震驚了整個濱河市朱庆,隨后出現的幾起案子,更是在濱河造成了極大的恐慌闷祥,老刑警劉巖娱颊,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡维蒙,警方通過查閱死者的電腦和手機掰吕,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颅痊,“玉大人殖熟,你說我怎么就攤上這事“呦欤” “怎么了菱属?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舰罚。 經常有香客問我纽门,道長胀莹,這世上最難降的妖魔是什么歧强? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任侦另,我火速辦了婚禮讶舰,結果婚禮上,老公的妹妹穿的比我還像新娘先匪。我一直安慰自己掸宛,他們只是感情好剪验,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布考传。 她就那樣靜靜地躺著吃型,像睡著了一般。 火紅的嫁衣襯著肌膚如雪僚楞。 梳的紋絲不亂的頭發(fā)上勤晚,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音泉褐,去河邊找鬼赐写。 笑死,一個胖子當著我的面吹牛膜赃,可吹牛的內容都是我干的血淌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼财剖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了癌淮?” 一聲冷哼從身側響起躺坟,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乳蓄,沒想到半個月后咪橙,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年美侦,在試婚紗的時候發(fā)現自己被綠了产舞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡菠剩,死狀恐怖易猫,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情具壮,我是刑警寧澤准颓,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站棺妓,受9級特大地震影響攘已,放射性物質發(fā)生泄漏。R本人自食惡果不足惜怜跑,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一样勃、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧性芬,春花似錦峡眶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至汽煮,卻和暖如春搏熄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暇赤。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工心例, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鞋囊。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓止后,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溜腐。 傳聞我的和親對象是個殘疾皇子译株,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容