前端固然要從 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
defer
的意思是延遲(Deferred),在 HTML4.01 規範 中規定:webpack
設置後,這個布爾屬性會向用戶代理提示該腳本將不會生成任何網頁內容(例如,JavaScript中不會生成 「document.write」),所以,用戶代理能夠繼續解析和渲染。
也就是說,在加上 defer
屬性後,瀏覽器會繼續解析、渲染畫面,而不會由於須要載入<script>
內的資源而卡住;實際執行時,會在 DOMContentLoaded
執行以前,由上到下的依照擺放順序觸發。git
聽起來很方便對吧?但要提醒各位,雖然 W3C 規範上說 defer
屬性會是一個布爾值,但 IE9 之前的版本是自定義的,即便寫成 <script defer="false">
仍然會有 defer
的效果,使用時要特別注意。程序員
又是你這個老不死的 IE……github
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
在主流的現代瀏覽器中,<script>
的屬性能夠加上 type="module"
。這時瀏覽器會認爲這個文件是一個JavaScript 模塊,其中的解析規則、執行環境會略有不一樣;這時 <script>
的默認行爲會像是 defer
同樣,在後臺下載,而且等待 DOM 解析、渲染完成以後纔會執行,因此 defer
屬性沒法在 type="module"
的狀況下發生做用。但一樣能夠經過 async
屬性使它在下載完成後即刻執行。
如今你應該明白這兩個屬性的特色了,那麼該怎樣正確地使用呢?
defer
因爲後臺載入、不打斷渲染及確保執行順序的特色,基本上在沒特殊需求的狀況下,在 <script>
中設置一下就好了;固然 <script>
自己的擺放順序仍是要稍微留心一下。
async
比較特別,由於在下載後會馬上執行,且不保證執行順序,通常常見的應用是設定在徹底獨立的小小模塊中,例如背景Logo、頁面廣告等,在避免形成使用者體驗變差的同時,儘可能早的產生效果。
如今前端開發大都經過 Webpack 等打包工具來輔助處理,不多有本身設定這些屬性的機會;開發者能夠經過 script-ext-html-webpack-plugin 等插件的幫助,將切分好的 Chunk 設定個別須要的 <script>
屬性。
async
及 defer
是 <script>
專屬的屬性,對於網頁中的其餘資源,能夠經過 <link>
的preload
、prefetch
屬性,來幫咱們延遲加載 將來才須要用到的資源。
雖然 <script>
的async
、defer
這些屬性的設置大都已經包含在現代框架的打包流程中了,但只有紮實的認識這些網頁最基礎的規範,才能明白本身寫出來的代碼最後會產生什麼效果。