現代的HTML網頁常會連結大量的「腳本(Script)」來讓網頁擁有各式各樣的功能。腳本功能固然強大,但若使用不當,很容易造成網頁的載入時間變長,影響使用者體驗。
script
標籤放置位置的影響
網頁瀏覽器在開啟HTML網頁的時候,會一邊解析HTML語法,一邊執行解析HTML後所得到的腳本。例如以下網頁,會逐漸在畫面上秀出1
、2
和3
,每個數字之間都有一個alert
函數擋著。
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
<script>alert('Hello');</script>
</head>
<body>
1
<script>
alert('Nah');
</script>
2
<script>
alert('Nah');
</script>
3
</body>
</html>
也就是說,如果有在腳本中作「尋找元素」的動作的話,就要注意腳本的位置是否是置於該元素出現之後。例如以下網頁,數字3
無法被腳本修改為33
。
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<div id="a">
1
</div>
<div id="b">
2
</div>
<script>
document.getElementById('a').innerHTML = '11';
document.getElementById('b').innerHTML = '22';
document.getElementById('c').innerHTML = '33';
</script>
<div id="c">
3
</div>
</body>
</html>
script
標籤可以搭配src
屬性,讓腳本能從成獨立的檔案讀入。例如以上網頁,可以修改如下:
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<div id="a">
1
</div>
<div id="b">
2
</div>
<script src="index.js"></script>
<div id="c">
3
</div>
</body>
</html>
document.getElementById('a').innerHTML = '11';
document.getElementById('b').innerHTML = '22';
document.getElementById('c').innerHTML = '33';
以上網頁中,當網頁瀏覽器從src
讀取外部的腳本時,它會先暫停當下HTML文件的解析,等到外部的遠端腳本下載好並執行完之後,才會繼續解析HTML。流程如下圖:
defer
屬性
替script
標籤加上defer
屬性,網頁瀏覽器在從src
讀取外部的腳本時,會繼續HTML文件的解析,直到HTML解析完後,才會回頭按照出現順序執行這些被加上defer
屬性的腳本,如果該腳本此時還沒被讀取完就會繼續等待它被讀取完才會執行它。流程如下圖:
例如以上網頁,可以再修改如下:
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<div id="a">
1
</div>
<div id="b">
2
</div>
<script src="index.js" defer></script>
<div id="c">
3
</div>
</body>
</html>
修改之後,以上網頁所使用的JavaScript腳本就不會有數字3
無法被修改為33
的問題了!
這邊要注意的是,defer
屬性只能和src
一同使用。如果是想要讓HTML內嵌的腳本擁有defer
屬性的話是沒有效果的哦!
async
屬性
替script
標籤加上async
屬性,網頁瀏覽器在從src
讀取外部的腳本時,會繼續HTML文件的解析,一旦外部的腳本讀取完,它會立刻被執行,執行腳本時會暫停解析HTML。流程如下圖:
例如以上網頁,可以再修改如下:
<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<div id="a">
1
</div>
<div id="b">
2
</div>
<script src="index.js" async></script>
<div id="c">
3
</div>
</body>
</html>
修改之後,以上網頁的數字3
就不一定會被修改為33
了!要看那個id為c
的div
元素是否能夠搶在腳本執行前被解析建立出來。
defer
+ async
屬性?
script
標籤可以同時加上defer
和async
屬性。一般來說,這樣寫的話只會有async
屬性的效果,但這樣可以讓不支援async
屬性的舊版網頁瀏覽器依然能使該腳本擁有defer
屬性的功能,使得網頁在開啟時不至於太慢。
所以哪個方式好?要加哪個屬性?還是都不加?
單以網頁載入速度來看,當然是defer
屬性最為優秀。只不過當我們把所有外部腳本都加上defer
屬性時,那麼網頁在載入完成之後,才會去執行這些腳本,若這些腳本會直接影響到畫面的話,那麼使用者會先看到爛掉的網頁畫面,並在網頁載入完成且腳本執行完之後,網頁畫面才能正常呈現。
也就是說,我們在選擇要替腳本加上哪個屬性時,應該先從defer
屬性開始考慮,如果它直接會影響到畫面,改用async
屬性會比較好一點。不過async
屬性的腳本並沒有順序性,所以還是得視情況與無async
屬性和defer
屬性的腳本搭配使用。