今天羣裏有人問爲何會出現腳本的加載順序與定義腳本順序不一致的問題,這個問題引發了個人好奇,通過一番調研,有了這篇文章。html
首先,W3C 推薦 script
腳本應該被當即加載和執行,其次,通過網絡搜索,我只發現了 1 例相同的問題,因此這個問題的真僞其實還有待進一步驗證,可是從邏輯上說,瀏覽器會並行加載靜態資源,對於 Chrome,能夠並行加載 6 個資源,若是其中一個資源獲取的比較緩慢,那麼會影響串行的下 6 個請求的發送,若是可以預先測試出 6 個通暢的請求,一併發送,那麼就能夠提高網絡加載的總體性能。但瀏覽器是否有這一層優化呢?目前我只見到這篇文章提到過瀏覽器彷佛有這個優化算法,可是並無在其餘地方獲得確認。算法
咱們有兩種方式使用 <script>
標籤:瀏覽器
src
屬性引入外部 JavaScript 靜態資源;<script>
開閉標籤內的 JavaScript 腳本;但其實本質上這兩種方式是一回事,其最終的目的就是讓瀏覽器在當前頁面執行 JavaScript 腳本,只不過對於前者而言多了一道工序:將服務器返回的 JavaScript 腳本內容插入 <script>
標籤內部,而後在執行它。緩存
所以,對於 <script>
標籤,咱們惟一關心的只有一點:JavaScript 腳本被執行的時機。服務器
在頁面中,咱們有兩處地方能夠放置 <script>
標籤:網絡
<head> ... </head>
head 標籤內部;<body> ... </body>
body 標籤內部;在 <head>
標籤中插入引用外部 JavaScript 會致使 <body>
標籤內的內容在 JavaScript 被徹底下載,解析,執行完畢後纔會被解析,這期間用戶會看到瀏覽器一片空白,所以會影響用戶體驗。(這是因爲瀏覽器從上至下解析 HTML 文檔,而 JavaScript 的下載,解析和執行會停止瀏覽器的解析過程)。併發
所以業界通行的作法是,將 script
標籤放置 <body>
底部,從而避免 JavaScript 阻塞頁面渲染。異步
但不管如何,咱們的 JS 腳本的執行順序是相同的:根據其在頁面中的位置決定前後順序。async
可是咱們能夠經過兩個屬性改變這一順序。性能
async
屬性是 HTML5 規範新推出的一個屬性,用來告知瀏覽器應該儘量的異步加載腳本。全部的瀏覽器都支持
該屬性。具備該屬性的腳本咱們既沒法得知它下載的時間,也沒法得知它執行的時機,咱們惟一知道的只有兩點:
⚠️ 注意,script
標籤必須有 src
屬性,且屬性值有效。
defer
屬性向瀏覽器指明瞭腳本被執行的時機:「文檔解析以後,DOMContentLoaded
事件被觸發以前(即 HTML 文檔被徹底加載和解析,無論樣式表,圖片或 iframe 是否加載完畢。恩,一個很微妙的時間 🤔)」,這裏須要注意的是,並非具備 defer
屬性的腳本會等待 DOMContentLoaded
的觸發,並趕在這以前執行,而是具備 defer
屬性的腳本會延遲 DOMContentLoaded
事件的觸發。
理論上講全部帶有 defer
屬性的腳本會按照在 HTML 中定義的順序被依次觸發,但遺憾的是實際中好像並不會這樣(此處有待作實驗進一步驗證)。
特別須要注意的是,帶有 defer
關鍵字的腳本也是以異步的形式被加載的。
⚠️ 注意,script
標籤必須有 src
屬性,且屬性值有效。
讓咱們總結一下,<script>
腳本默認被瀏覽器以必定順序並行下載,並按照定義的順序依次執行(在這期間,加載好的代碼安靜的待在瀏覽器緩存中,直到全部前置的腳本被加載和解析完成)。
咱們有兩種方式更改 <script>
的下載和執行時機,經過屬性 async
和 defer
,這兩個屬性都會採用異步的方式下載腳本,而他們的區別在於:添加了 async
屬性的腳本會被瀏覽器異步加載,但咱們沒法得知其被下載和執行的時間,而添加了 defer
屬性的腳本將會在文檔解析後,DOMContentLoaded
事件觸發前被執行。
最後,讓咱們再想一想這兩個屬性的使用時機:
async
:因爲咱們沒法知道添加了 async
屬性腳本的具體下載,執行時間,所以全部具備依賴關係或操做 DOM 元素的腳本都不適宜添加該屬性,反過來講,任何不具有依賴關係,不操做 DOM 的腳本均可以添加該屬性以節約腳本的下載時間,什麼類型的腳本知足上述要求呢?我能想到的有埋點類腳本,例如訪問統計腳本,廣告流量統計腳本等;defer
:該關鍵字給咱們的腳本一個完美的下載,執行時機,在 DOM 準備好以後,要不是現實中並不是依次加載帶有 defer
屬性的腳本,我幾乎就要建議你給全部腳本添加該屬性了,它既能夠保障咱們的腳本被異步加載,又可使咱們得到 DOM 操做的肯定性,除了內聯腳本,應該給每一個沒有依賴的腳本都添加該屬性。