script 擁有的屬性
- async:可選,表示應(yīng)該立即下載腳本,但不應(yīng)妨礙頁(yè)面中的其他操作,比如下載其他資源或等待加載其他腳本。只對(duì)外部腳本文件有效。
- charset:可選。表示通過(guò) src 屬性指定的代碼的字符集。由于大多數(shù)瀏覽器會(huì)忽略它的值,因此這個(gè)屬性很少有人用。
- defer:可選。表示腳本可以延遲到文檔完全被解析和顯示之后再執(zhí)行。只對(duì)外部腳本文件有效。IE7 及更早版本對(duì)嵌入腳本也支持這個(gè)屬性。
- language: 已廢棄。原來(lái)用于表示編寫(xiě)代碼使用的腳本語(yǔ)言(如 JavaScript 、 JavaScript1.2 或 VBScript )。大多數(shù)瀏覽器會(huì)忽略這個(gè)屬性,因此也沒(méi)有必要再用了。
- src:可選。表示包含要執(zhí)行代碼的外部文件。
- type:可選??梢钥闯墒?language 的替代屬性;表示編寫(xiě)代碼使用的腳本語(yǔ)言的內(nèi)容類型(也稱為 MIME 類型)。雖然 text/javascript 和 text/ecmascript 都已經(jīng)不被推薦使用,但人們一直以來(lái)使用的都還是 text/javascript 。實(shí)際上,服務(wù)器在傳送 JavaScript 文件時(shí)使用的 MIME 類型通常是 application/x–javascript ,但在 type 中設(shè)置這個(gè)值卻可能導(dǎo)致腳本被忽略。另外,在非IE瀏覽器中還可以使用以下值: application/javascript 和 application/ecmascript ??紤]到約定俗成和最大限度的瀏覽器兼容性,目前 type 屬性的值依舊還是 text/javascript 。不過(guò),這個(gè)屬性并不是必需的,如果沒(méi)有指定這個(gè)屬性,則其默認(rèn)值仍為text/javascript 。
引入方式 JavaScript 的兩種方式
內(nèi)聯(lián)形式
這種方式指的是在 html 文件中,添加一個(gè)<script></scritp>標(biāo)簽,然后將 JavaScript代碼直接寫(xiě)在里面
外置形式
外置形式是將 JavaScript 代碼寫(xiě)在外部的一個(gè)文件里面,在 html 文件中通過(guò) <script> 標(biāo)簽的 src 屬性引入
兩種引入形式的比較
對(duì)于這兩種方式,毫無(wú)疑問(wèn),外置形式明顯好于內(nèi)聯(lián)形式,主要表現(xiàn)為以下方面:
- 可維護(hù)性:外置 Javascript 文件可以被多個(gè)頁(yè)面調(diào)用而不用在每個(gè)頁(yè)面上反復(fù)地書(shū)寫(xiě).如果有需要改變的部分,你只需要在一處修改即可.所以外置JavaScript 導(dǎo)致代碼工作量減少,進(jìn)而使得維護(hù)手續(xù)也更加方便。
- 可緩存:瀏覽器能夠根據(jù)具體的設(shè)置緩存鏈接的所有外部 JavaScript文件。也就是說(shuō),如果有兩個(gè)頁(yè)面都使用同一個(gè)文件,那么這個(gè)文件只需下載一次。因此,最終結(jié)果就是能夠加快頁(yè)面加載的速度。
- 關(guān)注點(diǎn)分離:將 JavaScript 封裝在外部的.js文件遵循了關(guān)注點(diǎn)分離的法則.總體來(lái)說(shuō),分離 HTML,CSS 和 JavaScript 從而讓我們更容易操縱他們.而且如果是多名開(kāi)發(fā)者同步工作的話,這樣也更方便。
<script> 標(biāo)簽加載順序
如果要談<script> 標(biāo)簽加載順序問(wèn)題,首先要談的就是標(biāo)簽的位置,因?yàn)闃?biāo)簽的位置對(duì)于JavaScript加載順序來(lái)說(shuō)有著很重要的影響。
標(biāo)簽位置
<script> 標(biāo)簽的位置有兩種,一種是方式<head>元素里面,另外一種就是放在<body> 元素中頁(yè)面內(nèi)容的后面,下面將一一介紹這兩種形式:
<script> 標(biāo)簽放在<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>
<!-- 這里放內(nèi)容 -->
</body>
</html>
這是一種比較傳統(tǒng)的做法,目的就是把所有外部文件(包括 CSS 文件和 JavaScript 文件)的引用都放在相同的地方.可是,在文檔的 <head> 元素中包含所有 JavaScript 文件,意味著必須等到全部 JavaScript 代碼都被下載、解析和執(zhí)行完成以后,才能開(kāi)始呈現(xiàn)頁(yè)面的內(nèi)容(瀏覽器在遇到 <body> 標(biāo)簽時(shí)才開(kāi)始呈現(xiàn)內(nèi)容)。對(duì)于那些需要很多 JavaScript 代碼的頁(yè)面來(lái)說(shuō),這無(wú)疑會(huì)導(dǎo)致瀏覽器在呈現(xiàn)頁(yè)面時(shí)出現(xiàn)明顯的延遲,而延遲期間的瀏覽器窗口中將是一片空白。很明顯,這種做法有著很明顯的缺點(diǎn),特別是針對(duì)于現(xiàn)在的移動(dòng)端來(lái)說(shuō),如果超過(guò) 1s 還沒(méi)有內(nèi)容呈現(xiàn)的話將是一種很差的用戶體驗(yàn)。為了避免這個(gè)問(wèn)題,就有了下面這種加載方式。
<script> 標(biāo)簽放在<body> 元素中頁(yè)面內(nèi)容的后面
<!DOCTYPE html>
<html>
<head>
<title>Example HTML Page</title>
</head>
<body>
<!-- 這里放內(nèi)容 -->
<script type="text/javascript" src="example1.js"></script>
<script type="text/javascript" src="example2.js"></script>
</body>
</html>
對(duì)于這種方式,在解析包含的 JavaScript 代碼之前,頁(yè)面的內(nèi)容將完全呈現(xiàn)在瀏覽器中。而用戶也會(huì)因?yàn)闉g覽器窗口顯示空白頁(yè)面的時(shí)間縮短而感到打開(kāi)頁(yè)面的速度加快了
延遲加載
<script>的每個(gè)屬性設(shè)計(jì)來(lái)肯定都是有用的,下面我們就來(lái)說(shuō)一說(shuō) defer 屬性。
HTML 4.01 為<script> 標(biāo)簽定義了 'defer 屬性。這個(gè)屬性的用途是表明腳本在執(zhí)行時(shí)不會(huì)影響頁(yè)面的構(gòu)造。也就是說(shuō),腳本會(huì)被延遲到整個(gè)頁(yè)面都解析完畢后再運(yùn)行。因此,在 <script> 元素中設(shè)置defer 屬性,相當(dāng)于告訴瀏覽器立即下載,但延遲執(zhí)行,比如:
<!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 標(biāo)簽</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>
在這個(gè)例子中,雖然我們把 <script> 元素放在了文檔的 <head> 元素中,但其中包含的腳本將延遲到瀏覽器遇到 </html> 標(biāo)簽后再執(zhí)行。HTML5 規(guī)范要求腳本按照它們出現(xiàn)的先后順序執(zhí)行,因此第一個(gè)延遲腳本會(huì)先于第二個(gè)延遲腳本執(zhí)行,而這兩個(gè)腳本會(huì)先于 DOMContentLoaded 事件執(zhí)行。在現(xiàn)實(shí)當(dāng)中,延遲腳本并不一定會(huì)按照順序執(zhí)行,也不一定會(huì)在 DOMContentLoaded 事件觸發(fā)前執(zhí)行,因此最好只包含一個(gè)延遲腳本。
“在現(xiàn)實(shí)當(dāng)中,延遲腳本并不一定會(huì)按照順序執(zhí)行,也不一定會(huì)在 DOMContentLoaded 事件觸發(fā)前執(zhí)行,因此最好只包含一個(gè)延遲腳本?!?這段話是《JavaScript 高級(jí)程序設(shè)計(jì)(第三版)》中的一句話,糾結(jié)了很久。自己也嘗試寫(xiě)了一些例子,但反饋的結(jié)果都是:如果引入的 <script>標(biāo)簽 都使用了 defer 屬性,他們的執(zhí)行順序都是按照他們引入的順序來(lái)的。那么作者為什么會(huì)寫(xiě)上這一句話呢,個(gè)人感覺(jué)原因是:即使在 HTML5 規(guī)范中有這么一條,不一定所有的瀏覽器廠商都會(huì)遵照這個(gè)規(guī)定,可能某些瀏覽器廠商并沒(méi)有實(shí)現(xiàn)這個(gè)規(guī)范,但支持 defer 屬性,那么就會(huì)出現(xiàn)作者所描述的那種情況,所以為了安全起見(jiàn),在開(kāi)發(fā)中使用一個(gè) defer 是非常有必要的。
還有一點(diǎn)需要注意的是,defer 屬性只適用于外部腳本文件。
異步加載
說(shuō)完了延遲加載,然后我們?cè)僬f(shuō)下異步加載,即使用 async屬性。
HTML5 為 <script> 元素定義了 async 屬性。這個(gè)屬性與 defer 屬性類似,都用于改變處理腳本的行為。同樣與 defer 類似, async 只適用于外部腳本文件,并告訴瀏覽器立即下載文件,下載完成后立即執(zhí)行。但與 defer不同的是,標(biāo)記為 async 的腳本并不保證按照指定它們的先后順序執(zhí)行。例如:
<!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 標(biāo)簽</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 下載時(shí)間比較長(zhǎng),由于兩個(gè) <script> 標(biāo)簽都是異步執(zhí)行,互不干擾,因此 02.js 可能就會(huì)先于 01.js 執(zhí)行。因此,確保兩者之間互不依賴非常重要。指定 async 屬性的目的是不讓頁(yè)面等待兩個(gè)腳本下載和執(zhí)行,從而異步加載頁(yè)面其他內(nèi)容。為此,建議異步腳本不要在加載期間修改 DOM。
小結(jié)
- 所有 <script> 標(biāo)簽引進(jìn)的 JavaScript 會(huì)按照他們引入的順序依次被解析,在沒(méi)有使用 defer 或者 async 的情況下,只有在解析完前面 <script> 元素中的代碼之后,才會(huì)開(kāi)始解析后面 <script> 元素中的代碼。
- 由于瀏覽器會(huì)先解析完不使用 defer 屬性的<script> 元素中的代碼,然后再解析后面的內(nèi)容,所以一般應(yīng)該把<script> 元素放在頁(yè)面最后,即主要內(nèi)容后面, </body> 標(biāo)簽前面。
-
使用 defer 屬性可以讓腳本在文檔完全呈現(xiàn)之后再執(zhí)行,延遲腳本總是按照指定它們的順序執(zhí)行。
-使用 async 屬性可以表示當(dāng)前腳本不必等待其他腳本,也不必阻塞文檔呈現(xiàn)。不能保證異步腳本按照它們?cè)陧?yè)面中出現(xiàn)的順序執(zhí)行。
