前端頁面生命週期
前言
對頁面宣告週期的總結與回憶。
下文均為個人測試得出的結論,如有不對望指出。
正文
瞭解頁面宣告週期面前,需要了解幾個概念。
1.在頁面中dom載入與css載入是非同步的。但是呢,也不絕對,比如內聯css是同步的。
2.頁面中js載入與dom載入是同步的,但是也不絕對,這個比較複雜後文介紹。
先看下頁面的生命週期:
DomContentLoaded:dom載入完畢,但是外部資源可能沒有載入完畢,如樣式表、圖片資源等。
load:瀏覽器所有資源載入完畢。
beforunload/unload 當用戶離開頁面的時候觸發。
在我們寫demo的時候,往往這樣寫:
window.onload=function(){ }
可能會把我們初始化的資源都寫在onload中,但是在正常專案中,這樣寫的效率是不高的。
因為DomContentLoaded 這個時候就已經載入完了我們的Dom,這個時候就可以操作dom了。
但是因為樣式沒有載入,如果需要動態去獲取樣式的一些屬性,那麼就需要在onload中了。
DOMContentLoaded
用法:
document.addEventListener("DOMContentLoaded",ready);
function ready()
{
console.log("ready");
}
因為前面提及到dom載入與資源載入是非同步的,這時候不要去拿取資源,所以不要去拿取資原始檔。
前面提及UI渲染執行緒與JS引擎是互斥的,即使script 引用是以資原始檔src方式引用的,這時候依然會等待js的載入。
這時候就會有一些小問題,如果js載入時候過程過於漫長,那麼這樣使用者會不會以為宕機了,會不會懷疑有人刪庫跑路了?
如何提高使用者體驗度?
這時候就有async 與 defer 來幫助了。
<script type="text/javascript" src="demo_async.js" async="async"></script>
<script type="text/javascript" defer="defer"> alert(document.getElementById("p1").firstChild.nodeValue); </script>
對了,async只是針對外部資源,也就是對src有效,內聯js無效,defer對內聯與src都有效。
<script src="./test.js" async='async'>
</script>
<script>
document.addEventListener("DOMContentLoaded",ready);
function ready()
{
console.log("ready");
}
</script>
test.js
console.log("out script");
defer,我就不演示了,來介紹這兩個有什麼不同吧。
名稱 | 特性 |
---|---|
async | 比如有幾個帶有async的js檔案,這幾個執行檔案順序是不知道的,也就是說async的資原始檔是非同步的 |
defer | 會根據我們在頁面中書寫的順序執行 |
但是我建議不使用defer,看起來defer更好,實際上defer有致命性問題,只有 Internet Explorer 支援 defer 屬性。
看到這裡開心不開心?又少學了一樣東西。
但是呢,這個async 水有點深,怎麼說呢?
我剛才提及到了DOMContentLoaded會先執行,其實也不一定。
為何這麼說呢?比如我們頁面有很多不是async的js,然後可能async的資原始檔還有快取,當async載入完畢後,那麼不就要先執行async的資源。
所以async應該理解為獨立於dom生命週期載入執行。
舉一個async的一個好處的例子,也是我在網上看到的。
Firefox, Chrome和Opera會在DOMContentLoaded執行時自動補全表單。
但是當人們看到頁面的時候表單還沒有補齊。這時候就是DOMContentLoaded被前面執行的js給堵塞了。
這裡提及到另外一個現代主義瀏覽器的問題,為什麼我們將js指令碼放在body最後載入,是否可以減少到達DOMContentLoaded的時間?
其實不能,我們知道讓js放在dom元素可以讓docuemnt.getElementById 可以拿到元素,但是這依然可以在DOMContentLoaded中執行,似乎起不好什麼優化效果。
相對以前的瀏覽器,現在瀏覽器不對等待dom載入完畢後渲染,而是會先載入渲染一部分。
但是同樣有問題,比如我們要操作一些dom,但是dom太多,我們的js又在最後,那麼很有可能會亂。
所以我們開啟大型網頁的時候,常常見到嵌入到dom元素直接的。
像這樣:
<div></div>
<script></script>
<div></div>
但是,DOMContentLoaded依然要等到dom載入完畢。在這裡,其實DOMContentLoaded還沒有介紹完,因為還有ie的相容問題會在下面提及。
window.onload
這個不多介紹了,window物件上的onload事件在所有檔案包括樣式表,圖片和其他資源下載完畢後觸發。
window.onbeforeunload
有些網頁在我們在離開的時候,會問我們是否儲存。
就是用這個實現的。
支援情況:
IE、Safari 完美支援
Firefox、Chrome 不支援文字提醒資訊
Opera 不支援
window.onunload
使用者離開頁面的時候使用。
支援情況:
IE、Safari 完美支援
Firefox、Chrome 不支援文字提醒資訊
Opera 不支援
readyState
這個用的並不多,一般用來相容ie的。
但還有有些用的。比如我們使用async,非同步的時候希望去操作dom,不知道async是在載入後執行還是載入前,所以可以這樣。
if (document.readyState == 'loading') {
document.addEventListener('DOMContentLoaded', dosomething);
} else {
dosomething();
}
function dosomething() { /*.xxxxx..*/ }
回過來看下有什麼狀態:
一個document 的 Document.readyState 屬性描述了文件的載入狀態。
loading / 正在載入
document 仍在載入。
interactive / 可互動
文件已被解析,"正在載入"狀態結束,但是諸如影象,樣式表和框架之類的子資源仍在載入。
complete / 完成
文件和所有子資源已完成載入。表示 load 狀態的事件即將被觸發。
當這個屬性的值變化時,document 物件上的readystatechange 事件將被觸發。
這樣我們可以監聽到html的載入狀態了。
document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));
有一點上面可能產生誤解:是image的onload先執行呢?還是document.readyState 的complete 先執行呢?
這個不一定。因為如果image是最後一個載入的資源,回撥的可能就先是complete。如果image不是最後一個載入的資源,那麼就是image的onload了。
但是complete 回撥的時候肯定資源都載入完成了。
本來打算在這裡介紹ie相容的,因為Document.readyState是一個非常常用相容處理,但是篇幅問題,就下次吧。
總結
以上來自個人理解,如有不對,望指出