傳統的瀏覽器,對於靜態資源加載,會阻塞 HTML 解析器的線程進行,不管內聯仍是外鏈。
例如:html
<script src="test1.js"></script> <script src="test2.js"></script> <script src="test3.js"></script> <img src="img.png" />
傳統瀏覽器HTML解析器,會從test1.js 逐一解析到img.png,只個解析過程是同步的,只有當test1.js解析加載完成纔會到test2.js 順序加載。假設js文件加載時間須要1秒,img文件也須要1秒的時間,那麼除去頁面其餘階段的render時間不計,img圖片會是4秒以後才顯示給用戶。前端
相比傳統瀏覽器,當瀏覽器HTML解析器,遇到test1.js靜態資源的是,主線程中的解析器暫停解析,瀏覽器會新開啓一個解析器線程,去預加載後面的資源。假設js文件加載時間須要1秒,img文件也須要1秒的時間。當瀏覽器遇到第一個js文件test1.js的時候,會新開啓一個解析器線程,去預加載解析其餘靜態資源。除去頁面其餘階段的render時間不計,那麼img圖片只會是2秒以後才顯示給用戶。瀏覽器
但瀏覽器能作的僅僅是預解析和預加載,腳本的執行和 DOM 樹的構建仍然必須是線性的
緩存
<script src="test1.js"></script> <script> document.write('<script src="http://xxx.com/test2.js"><\/script>') </script> <script src="test3.js"></script>
這個例子中,因爲 test2.js 是經過 JS 代碼插入的,HTML 預解析器是看不到的,因此只有當 test1.js 下載並執行完畢,且第二個內聯的 script 執行完畢後,test2.js 纔會開始下載,也就是說,test2.js 不能和 test1.js 及 test3.js 並行下載了,從而致使頁面展示變慢,一樣假設每一個文件的下載時間都是 1 秒,那麼這三個文件下載執行完就須要兩秒,就由於 test2.js 不能預加載。在一個外鏈的 JS 文件好比 a.js 中執行 document.write("<script...) 也是相似的效果。異步
將資源轉成外鏈的方式加載。
以下async
<script src="test1.js"></script> <script> document.write('<script src="http://xxx.com/test2.js"><\/script>') </script> <script src="test3.js"></script>
改爲優化
<script src="test1.js"></script> <script src="test2.js"></script> <script src="test3.js"></script>
儘量,讓瀏覽器預解析器發現靜態資源文件。可是這並不意味着頁面的加載時間會大大減小。
假設test1.js的加載時間爲1秒,test2.js的加載時間爲10秒。即使test1.js以後的靜態資源參與了瀏覽器的預解析加載,爲了配合這句話「但瀏覽器能作的僅僅是預解析和預加載,腳本的執行和 DOM 樹的構建仍然必須是線性的
」。頁面始終會所有資源加載完以後才完成渲染,test2.js的10秒加載時間仍舊會讓頁面處於loading轉圈狀態。spa
對於非第三方的靜態資源的加載時間太長,應考慮前端資源的優化,這裏列出來可能多的優化方案,可是暫不作詳解線程
對於第三方的靜態資源文件,可使用async實現異步加載code
一句話歸納即是異步的 script 根本不會阻塞 HTML 解析器,也就用不到預解析了
目前大部分第三方庫都會支持async 異步 加載JS資源,而後去調用一個全局function 例如
<script src="test.js?callback=dosomething" async ></script>
async 的js資源,若是長時間pedding 會影響onload加載時間
ducument.write 在onload以前,插入執行document.write()都會給頁面插入內容,頁面onload完成以後,瀏覽器輸出流自動關閉;在此以後,任何一個對當前頁面進行操做的document.write()方法將打開—個新的輸出流
以下
結果是:
頁面onload完成以後,調用document.write
結果是: