繼上一篇“淺談js加載的時間線”下面來講一下關(guān)于異步加載的幾種方式
第一種延遲腳本defer
HTML4.01為<script>標(biāo)簽定義了defer屬性,這個屬性的用途是表明腳本在執(zhí)行時不會影響頁面的構(gòu)造,也就是說,腳本會被延遲到整個頁面都解析完畢后再運行,因此在<script>元素中設(shè)置defer屬性,相當(dāng)于告訴瀏覽器立即下載,但是延遲執(zhí)行
例如:在下面代碼中將外部js放到<head>里,然后設(shè)置defer="defer"屬性
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title>PISMDB</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<!--<!– Fonts–>-->
<link rel="stylesheet" type="text/css" href="./assets/css/reset.css" th:href="@{/static/assets/css/reset.css}">
<!--<!– Vendors–>-->
<link rel="stylesheet" type="text/css" href="./assets/vendors/bootstrap/bootstrap.min.css"
th:href="@{/static/assets/vendors/bootstrap/bootstrap.min.css}">
<!-- css -->
<link rel="stylesheet" type="text/css" href="./assets/css/main.css" th:href="@{/static/assets/css/main.css}">
<script src="./assets/vendors/_jquery/jquery-3.3.1.min.js" defer="defer"></script>
<script type="text/javascript" src="./assets/vendors/_jquery/jquery.SuperSlide.2.1.3.js" defer="defer"></script>
<script src="./assets/vendors/bootstrap/bootstrap.min.js" defer="defer"></script>
</head>
<body>
<!-- 定義頭部 -->
<div class="head">
...
在瀏覽器中執(zhí)行代碼通過network可查看,js的加載是按照順序來進行加載。

然后查看js的執(zhí)行順序
添加代碼:readyState代表dcument的狀態(tài),監(jiān)聽在文檔加載時的狀態(tài)變化,DOMContentLoaded事件在文檔解析完成后的某個時間進行觸發(fā)
<script type="text/javascript">
console.log(document.readyState);
document.onreadystatechange = function(){
console.log(document.readyState);
}
console.log("這是js執(zhí)行的");
document.addEventListener('DOMContentLoaded',function(){
console.log('DOMContentLoaded 觸發(fā)了');
},false)
瀏覽器執(zhí)行結(jié)果如下:js在DOMContentLoader觸發(fā)之前進行加載

在上面例子中,雖然我們把<script>元素放到了文檔的<head>元素中,在此處進行加載,但是執(zhí)行卻延遲到瀏覽器遇到</html>標(biāo)簽后再執(zhí)行,HTML5規(guī)范要求腳本按照他們出現(xiàn)的先后順序執(zhí)行,因此第一個延遲腳本會先于第二個延遲腳本執(zhí)行,第二個先于第三個延遲腳本執(zhí)行,而這三個腳本會先于DOMContentLoaded事件執(zhí)行。
在現(xiàn)實當(dāng)中,延遲腳本并不一定會按照順序執(zhí)行,也不一定會在DOMContentLoaded事件觸發(fā)前執(zhí)行,因此最好只包含一個延遲腳本
defer屬性只適用于外部腳本文件,這一點在HTML5中已經(jīng)明確規(guī)定,因此支持HTML5的實現(xiàn)會忽略給嵌入腳本設(shè)置的defer屬性。IE4~IE7還支持對嵌入腳本的defer屬性,但是IE8及之后的版本則完全支持HTML5規(guī)定的行為,因此將延遲腳本放在頁面底部是最佳選擇
第二種HTML5為<script>元素定義的async屬性
這個屬性與defer屬性類似,都用于改變處理腳本的行為,同樣與defer類似,只適用于外部腳本文件,并告訴瀏覽器立即下載文件,但與defer不同的是,標(biāo)記為async的腳本并不保證按照指定它們的先后順序執(zhí)行
例如:將外部js腳本設(shè)置為async屬性
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title>PISMDB</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<!--<!– Fonts–>-->
<link rel="stylesheet" type="text/css" href="./assets/css/reset.css" th:href="@{/static/assets/css/reset.css}">
<!--<!– Vendors–>-->
<link rel="stylesheet" type="text/css" href="./assets/vendors/bootstrap/bootstrap.min.css"
th:href="@{/static/assets/vendors/bootstrap/bootstrap.min.css}">
<!-- css -->
<link rel="stylesheet" type="text/css" href="./assets/css/main.css" th:href="@{/static/assets/css/main.css}">
<script src="./assets/vendors/_jquery/jquery-3.3.1.min.js" async></script>
<script type="text/javascript" src="./assets/vendors/_jquery/jquery.SuperSlide.2.1.3.js" async></script>
<script src="./assets/vendors/bootstrap/bootstrap.min.js" async></script>
<!-- -->
</head>
在以上代碼中,第二個腳本文件可能會在第一個腳本文件之前執(zhí)行,因此確保它們之間互不依賴非常重要
異步腳本一定會在頁面的load事件之前執(zhí)行,但可能會在DOMContentLoaded事件觸發(fā)之前或者之后執(zhí)行。
第三種動態(tài)的創(chuàng)建script的標(biāo)簽(可以解決兼容h5以及低版本ie的問題)
<script type="text/javascript">
function asyncLoaded(url,callback){
var script = document.createElement("script");
// script.src = url; 假如說網(wǎng)速非常好,直接執(zhí)行完成了,后面就監(jiān)聽不到狀態(tài)的改變了
if(script.readyState){
script.onreadystatechange = function(){
if(script.readyState == "complete" || script.readyState =="loaded"){
// 執(zhí)行某個函數(shù)
callback()
}
}
}else{
script.onload = function(){
// 執(zhí)行某個函數(shù)
callback()
}
}
script.src = url; //異步的過程
document.head.appendChild(script)
}
asyncLoaded("05.js",function(){
fn() //05.js中的函數(shù)
})
</script>
這種方式的缺點就是要清楚文件的加載順序,當(dāng)js文件多了,依賴關(guān)系復(fù)雜的時候,很難管理加載的依賴順序