1. 程式人生 > >script標籤載入順序(defer & async)

script標籤載入順序(defer & async)

script 擁有的屬性

  • async:可選,表示應該立即下載指令碼,但不應妨礙頁面中的其他操作,比如下載其他資源或等待載入其他指令碼。只對外部指令碼檔案有效。
  • charset:可選。表示通過 src 屬性指定的程式碼的字符集。由於大多數瀏覽器會忽略它的值,因此這個屬性很少有人用。
  • defer:可選。表示指令碼可以延遲到文件完全被解析和顯示之後再執行。只對外部指令碼檔案有效。IE7 及更早版本對嵌入指令碼也支援這個屬性。
  • language: 已廢棄。原來用於表示編寫程式碼使用的指令碼語言(如 JavaScript 、 JavaScript1.2 或 VBScript )。大多數瀏覽器會忽略這個屬性,因此也沒有必要再用了。
  • src:可選。表示包含要執行程式碼的外部檔案。
  • type:可選。可以看成是 language 的替代屬性;表示編寫程式碼使用的指令碼語言的內容型別(也稱為 MIME 型別)。雖然 text/javascript 和 text/ecmascript 都已經不被推薦使用,但人們一直以來使用的都還是 text/javascript 。實際上,伺服器在傳送 JavaScript 檔案時使用的 MIME 型別通常是 application/x–javascript ,但在 type 中設定這個值卻可能導致指令碼被忽略。另外,在非IE瀏覽器中還可以使用以下值: application/javascript 和 application/ecmascript 。考慮到約定俗成和最大限度的瀏覽器相容性,目前 type 屬性的值依舊還是 text/javascript 。不過,這個屬性並不是必需的,如果沒有指定這個屬性,則其預設值仍為text/javascript 。

引入方式 JavaScript 的兩種方式

內聯形式

這種方式指的是在 html 檔案中,新增一個<script></scritp>標籤,然後將 JavaScript程式碼直接寫在裡面

外接形式

外接形式是將 JavaScript 程式碼寫在外部的一個檔案裡面,在 html 檔案中通過 <script> 標籤的 src 屬性引入

兩種引入形式的比較

對於這兩種方式,毫無疑問,外接形式明顯好於內聯形式,主要表現為以下方面:

  • 可維護性:外接 Javascript 檔案可以被多個頁面呼叫而不用在每個頁面上反覆地書寫.如果有需要改變的部分,你只需要在一處修改即可.所以外接JavaScript 導致程式碼工作量減少,進而使得維護手續也更加方便。
  • 可快取:瀏覽器能夠根據具體的設定快取連結的所有外部 JavaScript檔案。也就是說,如果有兩個頁面都使用同一個檔案,那麼這個檔案只需下載一次。因此,最終結果就是能夠加快頁面載入的速度。
  • 關注點分離:將 JavaScript 封裝在外部的.js檔案遵循了關注點分離的法則.總體來說,分離 HTML,CSS 和 JavaScript 從而讓我們更容易操縱他們.而且如果是多名開發者同步工作的話,這樣也更方便。

<script> 標籤載入順序

如果要談<script> 標籤載入順序問題,首先要談的就是標籤的位置,因為標籤的位置對於JavaScript載入順序來說有著很重要的影響。

#####標籤位置
<script> 標籤的位置有兩種,一種是方式<head>元素裡面,另外一種就是放在<body> 元素中頁面內容的後面,下面將一一介紹這兩種形式:


<script> 標籤放在<head>元素裡
<!DOCTYPE html>
<html>

<head>
  <title>Example HTML Page</title>
  <script type="text/javascript" src="example1.js"></script>
  <script type="text/javascript" src="example2.js"></script>
</head>

<body>
  <!-- 這裡放內容 -->
</body>

</html>

這是一種比較傳統的做法,目的就是把所有外部檔案(包括 CSS 檔案和 JavaScript 檔案)的引用都放在相同的地方.可是,在文件的 <head> 元素中包含所有 JavaScript 檔案,意味著必須等到全部 JavaScript 程式碼都被下載、解析和執行完成以後,才能開始呈現頁面的內容(瀏覽器在遇到 <body> 標籤時才開始呈現內容)。對於那些需要很多 JavaScript 程式碼的頁面來說,這無疑會導致瀏覽器在呈現頁面時出現明顯的延遲,而延遲期間的瀏覽器視窗中將是一片空白。很明顯,這種做法有著很明顯的缺點,特別是針對於現在的移動端來說,如果超過 1s 還沒有內容呈現的話將是一種很差的使用者體驗。為了避免這個問題,就有了下面這種載入方式。

<script> 標籤放在<body> 元素中頁面內容的後面
<!DOCTYPE html>
<html>

<head>
  <title>Example HTML Page</title>
</head>

<body>
  <!-- 這裡放內容 -->
  <script type="text/javascript" src="example1.js"></script>
  <script type="text/javascript" src="example2.js"></script>
</body>

</html>

對於這種方式,在解析包含的 JavaScript 程式碼之前,頁面的內容將完全呈現在瀏覽器中。而使用者也會因為瀏覽器視窗顯示空白頁面的時間縮短而感到開啟頁面的速度加快了

延遲載入

<script>的每個屬性設計來肯定都是有用的,下面我們就來說一說 defer 屬性。
HTML 4.01 為<script> 標籤定義了 'defer 屬性。這個屬性的用途是表明指令碼在執行時不會影響頁面的構造。也就是說,指令碼會被延遲到整個頁面都解析完畢後再執行。因此,在 <script> 元素中設定defer 屬性,相當於告訴瀏覽器立即下載,但延遲執行,比如:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>script 標籤</title>
  <script defer="defer" type="text/javascript" src="./js/01.js"></script>
  <script defer="defer" type="text/javascript" src="./js/02.js"></script>
</head>

<body>
  <!-- content -->
  <script type="text/javascript" src="./js/03.js"></script>
</body>

</html>

在這個例子中,雖然我們把 <script> 元素放在了文件的 <head> 元素中,但其中包含的指令碼將延遲到瀏覽器遇到 標籤後再執行。HTML5 規範要求指令碼按照它們出現的先後順序執行,因此第一個延遲指令碼會先於第二個延遲指令碼執行,而這兩個指令碼會先於 DOMContentLoaded 事件執行。在現實當中,延遲指令碼並不一定會按照順序執行,也不一定會在 DOMContentLoaded 事件觸發前執行,因此最好只包含一個延遲指令碼。

“在現實當中,延遲指令碼並不一定會按照順序執行,也不一定會在 DOMContentLoaded 事件觸發前執行,因此最好只包含一個延遲指令碼。” 這段話是《JavaScript 高階程式設計(第三版)》中的一句話,糾結了很久。自己也嘗試寫了一些例子,但反饋的結果都是:如果引入的 <script>標籤 都使用了 defer 屬性,他們的執行順序都是按照他們引入的順序來的。那麼作者為什麼會寫上這一句話呢,個人感覺原因是:即使在 HTML5 規範中有這麼一條,不一定所有的瀏覽器廠商都會遵照這個規定,可能某些瀏覽器廠商並沒有實現這個規範,但支援 defer 屬性,那麼就會出現作者所描述的那種情況,所以為了安全起見,在開發中使用一個 defer 是非常有必要的。

還有一點需要注意的是,defer 屬性只適用於外部指令碼檔案。

非同步載入

說完了延遲載入,然後我們再說下非同步載入,即使用 async屬性。
HTML5 為 <script> 元素定義了 async 屬性。這個屬性與 defer 屬性類似,都用於改變處理指令碼的行為。同樣與 defer 類似, async 只適用於外部指令碼檔案,並告訴瀏覽器立即下載檔案,下載完成後立即執行。但與 defer不同的是,標記為 async 的指令碼並不保證按照指定它們的先後順序執行。例如:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>script 標籤</title>
  <script async type="text/javascript" src="./js/01.js"></script>
  <script async type="text/javascript" src="./js/02.js"></script>
</head>

<body>
  <!-- content -->
</body>

</html>

在以上程式碼中,可能由於 01.js 下載時間比較長,由於兩個 <script> 標籤都是非同步執行,互不干擾,因此 02.js 可能就會先於 01.js 執行。因此,確保兩者之間互不依賴非常重要。指定 async 屬性的目的是不讓頁面等待兩個指令碼下載和執行,從而非同步載入頁面其他內容。為此,建議非同步指令碼不要在載入期間修改 DOM。

小結

  • 所有 <script> 標籤引進的 JavaScript 會按照他們引入的順序依次被解析,在沒有使用 defer 或者 async 的情況下,只有在解析完前面 &ltscript> 元素中的程式碼之後,才會開始解析後面 &ltscript> 元素中的程式碼。
  • 由於瀏覽器會先解析完不使用 defer 屬性的&ltscript> 元素中的程式碼,然後再解析後面的內容,所以一般應該把&ltscript> 元素放在頁面最後,即主要內容後面, &lt/body> 標籤前面。
  • 使用 defer 屬性可以讓指令碼在文件完全呈現之後再執行,延遲指令碼總是按照指定它們的順序執行
    -使用 async 屬性可以表示當前指令碼不必等待其他指令碼,也不必阻塞文件呈現。不能保證非同步指令碼按照它們在頁面中出現的順序執行。

image.png

原文 https://www.javascriptcn.com/read-9419.html