1. 程式人生 > >前端頁面生命週期

前端頁面生命週期

前言

對頁面宣告週期的總結與回憶。

下文均為個人測試得出的結論,如有不對望指出。

正文

瞭解頁面宣告週期面前,需要了解幾個概念。

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是一個非常常用相容處理,但是篇幅問題,就下次吧。

總結

以上來自個人理解,如有不對,望指出