淺析script 標籤的 async 和 defer 屬性

前端固然要從 HTML 開始,咱們來聊聊在 script 標籤中加上 async/defer 時的功能及差別。javascript

都明白的道理

咱們都知道,瀏覽器解析 HTML 是一行一行按照順序向後讀取的,在傳統的寫法中,當瀏覽器讀到 <script> 時,便會暫停解析 DOM,同時當即開始下載 <script> 中定義的資源,並在下載完成後馬上執行。因爲這樣的特性,可能會形成 DOM 樹在尚未徹底解析時就開始執行 JavaScript,須要操做 DOM 的程序可能所以沒法正確執行,從而形成許多問題;或是因爲 <script> 中的資源下載、執行時間過程,用戶會卡在白畫面,並會產生以爲網站太慢很差用之類的體驗。html

而解決方法也很簡單,咱們須要把 <script> 標籤的位置都放到 <body> 的最後一行來避免 DOM 樹解析不徹底的問題,可是在複雜的網站中, HTML、JavaScript 的個頭都很大,須要等到整個 DOM 樹都載入完成纔開始下載 <script> 內的資源,從網站讀取完成到可操做,會產生明顯的延遲感。前端

那這種問題該怎麼解決呢?html5

從HTML4 開始,<script> 多了 defer 屬性,而 HTML5 則多了 async,二者都是用來幫助開發者控制 <script> 內資源的載入及執行順序,以及避免 DOM 的解析被資源下載卡住的。java

image.png

defer

defer 的意思是延遲(Deferred),在 HTML4.01 規範 中規定:webpack

設置後,這個布爾屬性會向用戶代理提示該腳本將不會生成任何網頁內容(例如,JavaScript中不會生成 「document.write」),所以,用戶代理能夠繼續解析和渲染。

也就是說,在加上 defer 屬性後,瀏覽器會繼續解析、渲染畫面,而不會由於須要載入<script> 內的資源而卡住;實際執行時,會在 DOMContentLoaded 執行以前,由上到下的依照擺放順序觸發。git

聽起來很方便對吧?但要提醒各位,雖然 W3C 規範上說 defer 屬性會是一個布爾值,但 IE9 之前的版本是自定義的,即便寫成 <script defer="false"> 仍然會有 defer 的效果,使用時要特別注意。程序員

又是你這個老不死的 IE……github

async

async 的意思是異步(Asynchronous),在 HTML5 規範 中規定:web

…若是存在 async 屬性,則腳本將會在可用時當即異步執行 …

<script> 標籤中加上 async 屬性後,與defer 的相同點是也會在後臺執行下載,但不一樣的是當下載完成會立刻暫停 DOM 解析(若是尚未解析完成的話),並開始執行 JavaScript。由於下載完成後會當即執行,加上 async 屬性後,就沒法保證執行順序了。

這個屬性在標準中,同時也支持經過 JavaScript 動態插入 <script> 的狀況。例如:

const script = document.createElement('script')
script.src = "/something/awesome.js"
document.body.append(script)

動態建立的 <script>,默認就是異步載入;但能夠經過設定屬性將它關閉:

script.async = false

type=」module」

在主流的現代瀏覽器中,<script> 的屬性能夠加上 type="module"。這時瀏覽器會認爲這個文件是一個JavaScript 模塊,其中的解析規則、執行環境會略有不一樣;這時 <script> 的默認行爲會像是 defer 同樣,在後臺下載,而且等待 DOM 解析、渲染完成以後纔會執行,因此 defer 屬性沒法在 type="module" 的狀況下發生做用。但一樣能夠經過 async 屬性使它在下載完成後即刻執行。

用法

如今你應該明白這兩個屬性的特色了,那麼該怎樣正確地使用呢?

defer 因爲後臺載入、不打斷渲染及確保執行順序的特色,基本上在沒特殊需求的狀況下,在 <script> 中設置一下就好了;固然 <script> 自己的擺放順序仍是要稍微留心一下。

async 比較特別,由於在下載後會馬上執行,且不保證執行順序,通常常見的應用是設定在徹底獨立的小小模塊中,例如背景Logo、頁面廣告等,在避免形成使用者體驗變差的同時,儘可能早的產生效果。

如今前端開發大都經過 Webpack 等打包工具來輔助處理,不多有本身設定這些屬性的機會;開發者能夠經過 script-ext-html-webpack-plugin 等插件的幫助,將切分好的 Chunk 設定個別須要的 <script> 屬性。

總結

asyncdefer<script> 專屬的屬性,對於網頁中的其餘資源,能夠經過 <link>preloadprefetch 屬性,來幫咱們延遲加載 將來才須要用到的資源。

雖然 <script>asyncdefer 這些屬性的設置大都已經包含在現代框架的打包流程中了,但只有紮實的認識這些網頁最基礎的規範,才能明白本身寫出來的代碼最後會產生什麼效果。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索