HTML 中 JavaScript 的載入問題
前言
一般在瀏覽器中對於HTML
的解析順序是由上而下的,所以在傳統的網頁設計中我們一般都會將<script>
標籤放置在<head></head>
中,這樣能確保JavaScript
盡早被執行到。
但是隨著近年來網頁不再只是網頁,而漸漸成為了Web Application
,因為如此所以使用到的JavaScript
就變得越來越大,這時候JavaScript
的載入就是個問題。
阻塞 (Blocking)
在傳統的瀏覽器中,每當解析到了<script>
標籤時瀏覽器的繪製就會停頓下來,然後去下載並執行JavaScript
中的內容,過多的JavaScript
內容會造成阻塞問題,便是瀏覽器停頓一段時間等待JavaScript
都執行完畢後,才開始繪製剩下的網頁內容。
上面提到,一般會將<script>
標前放置在<head></head>
中,這會造成網頁在一片空白的情況下停頓許久。
<html>
<head>
<script></script>
<script></script>
</head>
<body>
<!--something-->
</body>
</html>
所以現在會建議將<script>
放置在<body></body>
的尾端,等待瀏覽器將大部分的內容都繪製出來之後再去執行JavaScript
,這樣比較不會造成使用者的觀感不佳。
<html>
<head>
</head>
<body>
<!--something-->
<script></script>
<script></script>
</body>
</html>
async, defer 屬性
雖然可以將<script>
放置在<body></body>
的尾端,來減少使用者觀感不佳,但是實際上放置在尾端還是會有阻塞(Blocking)的問題,這個問題依舊沒有解決。
所以在HTML5
終究提出了新的async
以及defer
屬性來解決阻塞(Blocking)的問題,在<script>
中加上這兩個屬性就可以讓有支援的瀏覽器,以非同步的方式下載JavaScript
並執行其內容。
<script src="file.js"></script>
<script src="file.js" defer></script>
<script src="file.js" async></script>
<script>
,停止網頁繪製,等待JavaScript
下載並執行完,再繼續網頁繪製。
<script defer>
,不停止網頁繪製,JavaScript
的下載會同時進行,等待網頁繪製完成會再執行JavaScript
。
<script async>
,不停止網頁繪製,JavaScript
的下載會同時進行,當JavaScript
下載完成後,再停止網頁繪製並執行JavaScript
,等待執行完成後再繼續網頁繪製。
Peter Beverloo在他的網誌上有繪製一張時序圖,用來說明這兩個新的屬性與傳統的載入方式有什麼不同,可參考下方連結。
RequireJS
async
與defer
屬性可以解決阻塞問題,但也引起了一個新的問題,那就是以非同步方是執行的JavaScript
的執行順序並不是依照<script>
在HTML上的順序,而是誰先下載好就先執行誰,如果你的JavaScript
的設計在執行上有相依性的問題就有機會因為執行順序的不同而爆炸。
因此RequireJS
的出現能夠解決此問題,它可以以非同步方式載入JavaScript
,並且也可以解決JavaScript
檔案之前的相依性問題,並讓JavaScript
模組化。
在這並沒有要說明使用方式,可以參考網路上現有的文件,如下。
- How to get started with RequireJS
- 初探 RequireJS
- 實用 JavaScript 工具庫:RequireJS
- Javascript模块化编程(三):require.js的用法
小結
上面提出的幾種解決方法,但是在實作時也不用全用上,依照當下的實作規模來做取捨,雖然沒有提到但是在製作Mobile Web
時要更加注意JavaScript
載入(以及其他多媒體檔案的載入),因3G
或是Wifi
並沒有比有線網路來的快速,所以網頁讀取時的停頓感會更加的放大,這點必須注意。