零、序言
vue 用多了,自然離不開生命周期。最近突發奇想,加上之前看過的文章中關於 script 標簽中的 async 和 defer 的搗糨糊,決定整理一下,攻克這個模糊點。
當然,最多的還是與 script 標簽相關的前兩個周期,后倆個周期並沒有過多的復雜點,因此會着重描述前兩個。
參考文章:
1.頁面生命周期
2.HTML,javascript,image等加載,DOM解析,js執行生命周期
一、頁面周期
1.DOMContentLoaded - 瀏覽器已經加載了 Html, DOM 樹已經構建完畢,但是 img 和外部樣式表等資源可能還沒有下載完畢。
2.load - 瀏覽器已經完全加載了所有資源。
3.beforeunload - 用戶即將離開頁面。
4.unload - 用戶離開頁面。
每一個事件都有特定的用途,前兩個如其名,beforeunload 會給用戶彈出個確認框,unload 則不會。
二、DOMContentLoaded
DOMContentLoaded 事件由 document 對象觸發,我們可以使用 addEventListener 來觸發。
正如前文所說,在這個事件觸發的時候,我們如果獲取某些 img 的寬度和高度的話,得到的可能是0。
當然這里是有陷阱的。
1. 與腳本(<script />)
首先有一點,瀏覽器的 UI 渲染線程和 JS 引擎是互斥的,當 JS 引擎執行時 UI 線程會被掛起。因此,當瀏覽器在解析 HTML 時遇到 <script /> 時,將不會繼續構建 DOM 樹,轉而取解析、執行腳本,所以 DOMContentLoaded 有可能在所有腳本執行完畢之后觸發。
外部腳本(通過 src 引入)的加載和解析和自帶的一樣會暫停 DOM 樹的構建,這里 DOMContentLoaded 會等待。
不過有兩個特殊的情況,如果外部腳本上帶有 async 或者 defer 屬性,那么瀏覽器會繼續執行 DOM 解析而不需要等待腳本的完全執行,所以這一直時外部腳本的優化方案之一。(async 和 defer 屬性僅對外部腳本起作用, 當 src 不存在的時候會被自動忽略)
關於 async 和 defer 與 DOM 的解析順序如下圖所示:
既然提到了 async 和 defer, 也順帶整理一下他們的異同:
async | defer | |
順序 | 帶有 async 的腳本是優先執行先加載完的腳本,即他們在頁面中的順序並不保證他們的執行順序。 | 帶有 defer 的腳本時按照他們在頁面中出現的順序依次執行。 |
DOMContentLoaded | 帶有async 的腳本也許會在頁面沒有完全下載完之前就加載,這種情況會在腳本很小或本緩存,並且頁面很大的情況下發生。 |
帶有defer 的腳本會在頁面加載和解析完畢后執行,剛好在 DOMContentLoaded 之前執行。 |
所以 async 用在完全沒有依賴和被依賴的腳本上。
三、load
load 事件是在 window 對象上的,這與 DOMContentLoaded 不同。該事件在所有文件包括樣式表,圖片和其他資源下載完畢后觸發。既然這樣規定,自然該干啥干啥,沒什么明顯的陷阱。
四、beforeunload
如果用戶即將離開頁面或關閉窗口時,beforeunload 事件將會被觸發以進行額外的確認。舉個例子:
window.onbeforeunload = function() { return "There are unsaved changes. Leave now?"; };
當然,如果在 chrome 和 firefox 瀏覽器中會忽略返回的自定義的字符串,這是出於安全考慮的。
五、unload
unload 事件與 load 事件一樣,是在 window 對象上的,觸發時間為用戶關閉該頁面的時候,我們可以做一些不存在延時的任務,比如關閉彈層等等。
六、readyState
document.readyState 這個只讀屬性可以告訴程序當前文檔加載到哪一個步驟,它有三個值:
1. loading - 加載,document 仍在加載中;
2. interactive - 互動,文檔已經完成加載,文檔已被解析,但是諸如圖像,樣式表和框架之類的子資源仍在加載。
3. complete - 文檔和所有子資源已完成加載。狀態表示 load
事件即將被觸發。
而這個屬性的每次改變同樣有一個事件可以監聽:
document.addEventListener('readystatechange', () => console.log(document.readyState));
不過這個 change 事件很少會被用到,可能出現的地方在某些第三方類庫中判斷一些依賴關系等地方。不詳述,具體可移步文首的文章或者 MDN。