本文實例講述了js實現動態加載腳本的方法。分享給你們供你們參考,具體以下:javascript
最近公司的前端地圖產品須要作一下模塊劃分,但願用戶用到哪一塊的功能再加載哪一塊的模塊,這樣能夠提升用戶體驗。html
因此處處查資料研究js動態腳本的加載,不過真讓人傷心啊!,網上幾乎都是同一篇文章,4種方法,討厭其中拷貝別人成果的人,也不加個原文的連接。哎!關鍵是最後一種方法還有點錯誤。通過兩天的研究查閱資料,在這裏和你們分享一下。前端
首先咱們須要一個被加載的js文件,我在一個固定文件夾下建立了一個package.js,打開後在裏面寫一個方法functionOne,很簡單,代碼以下:java
1
2
3
|
function
functionOne(){
alert(
"成功加載"
);
}
|
後面的html文件都建立在同一個目錄下。node
方法一:直接document.writeajax
在同一個文件夾下面建立一個function1.html,代碼以下:瀏覽器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
>
function
init()
{
//加載js腳本
document.write(
"<script src='package.js'><\/script>"
);
//加載一個按鈕
document.write(
"<input type=\"button\" value=\"測試運行效果\" onclick=\"operation()\"\/>"
);
//若是立刻使用會找不到,由於尚未加載進來,此處會報錯
functionOne();
}
function
operation()
{
//能夠運行,顯示「成功加載」
functionOne();
}
</script>
</head>
<body>
<input type=
"button"
value=
"初始化加載"
onclick=
"init()"
/>
</body>
</html>
|
經過document.write的方式能夠往頁面寫入腳本,如代碼所示,點擊按鈕「初始化加載」後能夠加載package.js文件,可是當即運行裏面的方法functionOne會找不到此方法,報告錯誤,而點擊第二個按鈕(經過document.write動態建立的「測試運行效果」)發現能夠執行,此時腳本已經加載完畢了。因爲這種方式是異步加載(一邊繼續執行後面的代碼,一邊額外開一個線程執行須要加載的腳本),而且document.write會重寫界面,明顯不實用。緩存
方法二:動態改變已有script的src屬性服務器
在同一個文件夾下面建立一個function2.html,代碼以下:app
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
id=
"yy"
src=
""
></script>
<script type=
"text/javascript"
>
function
init()
{
yy.src =
"package.js"
;
//若是立刻使用會找不到,由於尚未加載進來,此處會報錯
functionOne();
}
function
operation()
{
//能夠運行,顯示「成功加載」
functionOne();
}
</script>
</head>
<body>
<input type=
"button"
value=
"測試按鈕"
onclick=
"init()"
/>
<input type=
"button"
value=
"測試運行效果"
onclick=
"operation()"
/>
</body>
</html>
|
此種方法的好處在於不會改變界面元素,不至於重寫界面元素,可是一樣是異步加載,會有一樣的問題。
方法三:動態建立script元素(異步)
在同一個文件夾下面建立一個function3.html,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
>
function
init()
{
var
myScript= document.createElement(
"script"
);
myScript.type =
"text/javascript"
;
myScript.src=
"package.js"
;
document.body.appendChild(myScript);
//若是立刻使用會找不到,由於尚未加載進來
functionOne();
}
function
operation()
{
//能夠運行,顯示「成功加載」
functionOne();
}
</script>
</head>
<body>
<input type=
"button"
value=
"測試按鈕"
onclick=
"init()"
/>
<input type=
"button"
value=
"測試運行效果"
onclick=
"operation()"
/>
</body>
</html>
|
此辦法的優點相對於第二種而言就是不須要最開始就在界面寫一個script標籤,缺點仍是異步加載,存在一樣的問題。
這三種方法都是異步執行的,因此在加載這些腳本的同時,主頁面的腳本繼續運行,若是用以上的方法,那下面的代碼將得不到預期的效果。
不過能夠在functionOne前面加一個alert就能夠堵塞一下主頁面腳本的運行,而後你發現functionOne就能夠運行了,或者你的後期代碼須要在另外一個按鈕下執行,一步一步的來,要不就定義一個計時器,在固定時間後再執行後面的代碼,不過在項目裏面確定不可能使用這些方法。
其實第三種方法改一點就變成同步加載了。
方法四:動態建立script元素(同步)
在同一個文件夾下面建立一個function4.html,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
>
function
init()
{
var
myScript= document.createElement(
"script"
);
myScript.type =
"text/javascript"
;
myScript.appendChild(document.createTextNode(
"function functionOne(){alert(\"成功運行\"); }"
));
document.body.appendChild(myScript);
//此處發現能夠運行
functionOne();
}
</script>
</head>
<body>
<input type=
"button"
value=
"測試按鈕"
onclick=
"init()"
/>
</body>
</html>
|
此方法並無加載外部的js文件,而是給myScript添加了子項。在Firefox、Safari、Chrome、Opera和IE9中,這些代碼能夠正常運行。可是在IE8以及如下的版本中會致使錯誤。IE將<script>視爲一個特殊的元素,不容許DOM訪問其子節點。不過能夠用<script>元素的text屬性來制定js代碼,想下面的例子這樣:
1
2
3
4
5
6
|
var
myScript= document.createElement(
"script"
);
myScript.type =
"text/javascript"
;
myScript.text =
"function functionOne(){alert(\"成功運行\"); }"
;
document.body.appendChild(myScript);
//此處能夠運行
functionOne();
|
通過這樣修改以後的代碼能夠在IE、Firefox、Opera和Safari3及以後版本中運行。Safari3.0以前的版本雖然不能正確地支持text屬性,但卻容許使用文本節點技術來指定代碼。若是須要兼容早期版本的Safari,可使用下面代碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
var
myScript= document.createElement(
"script"
);
myScript.type =
"text/javascript"
;
var
code =
"function functionOne(){alert(\"成功運行\"); }"
;
try
{
myScript.appendChild(document.createTextNode(code));
}
catch
(ex){
myScript.text = code;
}
document.body.appendChild(myScript);
//此處發現能夠運行
functionOne();
|
這裏,首先嚐試標準的DOM文本節點方法,由於除了IE8以及如下,全部瀏覽器都支持這種方式。若是這行代碼拋出了錯誤,那麼說明是IE8以及如下,因而就必須使用text屬性了。整個過程能夠用如下函數來表示:
1
2
3
4
5
6
7
8
9
10
11
12
|
function
loadScriptString(code)
{
var
myScript= document.createElement(
"script"
);
myScript.type =
"text/javascript"
;
try
{
myScript.appendChild(document.createTextNode(code));
}
catch
(ex){
myScript.text = code;
}
document.body.appendChild(myScript);
}
|
而後你能夠在其餘地方使用此方法來加載須要使用的代碼。實際上,這樣執行代碼與在全局做用於中把相同字符串傳遞給eval()是同樣的。可是咱們這裏只能使用字符串形式的代碼,也有侷限性,用戶通常但願提供的方法形如loadScriptAddress("package.js")的方式,因此咱們還須要繼續討論。
方法五:XMLHttpRequest/ActiveXObject異步加載
在同一個文件夾下面建立一個function5.html,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
>
function
init()
{
//加載package.js文件,設置script的id爲yy
ajaxPage(
"yy"
,
"package.js"
);
//此方法爲package.js裏面的方法,此處執行方法成功
functionOne();
}
function
ajaxPage(sId,url)
{
var
oXmlHttp = getHttpRequest();
oXmlHttp.onreadystatechange =
function
()
{
//4表明數據發送完畢
if
( oXmlHttp.readyState == 4 )
{
//0爲訪問的本地,200表明訪問服務器成功,304表明沒作修改訪問的是緩存
if
(oXmlHttp.status == 200 || oXmlHttp.status == 0 || oXmlHttp.status == 304)
{
includeJS(sId,oXmlHttp.responseText);
}
else
{
}
}
}
oXmlHttp.open(
"GET"
,url,
true
);
oXmlHttp.send(
null
);
}
function
getHttpRequest()
{
if
(window.ActiveXObject)
//IE
{
return
new
ActiveXObject(
"MsXml2.XmlHttp"
);
}
else
if
(window.XMLHttpRequest)
//其餘
{
return
new
XMLHttpRequest();
}
}
function
includeJS(sId,source)
{
if
((source !=
null
)&&(!document.getElementById(sId)))
{
var
myHead = document.getElementsByTagName(
"HEAD"
).item(0);
var
myScript = document.createElement(
"script"
);
myScript.language =
"javascript"
;
myScript.type =
"text/javascript"
;
myScript.id = sId;
try
{
myScript.appendChild(document.createTextNode(source));
}
catch
(ex){
myScript.text = source;
}
myHead.appendChild( myScript );
}
}
</script>
</head>
<body>
<input type=
"button"
value=
"測試按鈕"
onclick=
"init()"
/>
</body>
</html>
|
ActiveXObject只有IE裏面纔有,其餘瀏覽器大部分支持XMLHttpRequest,經過此辦法咱們能夠實現動態加載腳本了,不過是異步加載,也無法運行functionOne,第二次就能夠運行了,可是惋惜的是在IE、Firefox、Safari下能夠運行,在Opera、Chrome下會出錯,Chrome下的錯誤以下:
不過只要發佈以後在Chrome和Opera下就不會出現錯誤了。
其實這裏把open裏面設置爲false就是同步加載了,同步加載不須要設置onreadystatechange事件。
方法六:XMLHttpRequest/ActiveXObject同步加載
在這裏我把一些狀況考慮在內,寫成了一個方法,封裝爲loadJS.js,方便之後直接調用,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
/**
* 同步加載js腳本
* @param id 須要設置的<script>標籤的id
* @param url js文件的相對路徑或絕對路徑
* @return {Boolean} 返回是否加載成功,true表明成功,false表明失敗
*/
function
loadJS(id,url){
var
xmlHttp =
null
;
if
(window.ActiveXObject)
//IE
{
try
{
//IE6以及之後版本中可使用
xmlHttp =
new
ActiveXObject(
"Msxml2.XMLHTTP"
);
}
catch
(e) {
//IE5.5以及之後版本可使用
xmlHttp =
new
ActiveXObject(
"Microsoft.XMLHTTP"
);
}
}
else
if
(window.XMLHttpRequest)
//Firefox,Opera 8.0+,Safari,Chrome
{
xmlHttp =
new
XMLHttpRequest();
}
//採用同步加載
xmlHttp.open(
"GET"
,url,
false
);
//發送同步請求,若是瀏覽器爲Chrome或Opera,必須發佈後才能運行,否則會報錯
xmlHttp.send(
null
);
//4表明數據發送完畢
if
( xmlHttp.readyState == 4 )
{
//0爲訪問的本地,200到300表明訪問服務器成功,304表明沒作修改訪問的是緩存
if
((xmlHttp.status >= 200 && xmlHttp.status <300) || xmlHttp.status == 0 || xmlHttp.status == 304)
{
var
myHead = document.getElementsByTagName(
"HEAD"
).item(0);
var
myScript = document.createElement(
"script"
);
myScript.language =
"javascript"
;
myScript.type =
"text/javascript"
;
myScript.id = id;
try
{
//IE8以及如下不支持這種方式,須要經過text屬性來設置
myScript.appendChild(document.createTextNode(xmlHttp.responseText));
}
catch
(ex){
myScript.text = xmlHttp.responseText;
}
myHead.appendChild( myScript );
return
true
;
}
else
{
return
false
;
}
}
else
{
return
false
;
}
}
|
此處考慮到了瀏覽器的兼容性以及當爲Chrome、Opera時必須是發佈,註釋仍是寫的比較清楚的,之後須要加載某個js文件時,只須要一句話就好了,如loadJS("myJS","package.js")。方便實用。
若是想要實現不發佈還非要兼容全部瀏覽器,至少我還沒找出這樣的同步加載的辦法,咱們只能經過異步加載開出回調函數來實現。
方法七:回調函數方式
在同一個文件夾下面建立一個function7.html,代碼以下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<html>
<head>
<title></title>
<script type=
"text/javascript"
>
function
init()
{
//加載package.js文件,設置script的id爲yy
loadJs(
"yy"
,
"package.js"
,callbackFunction);
}
function
callbackFunction()
{
functionOne();
}
function
loadJs(sid,jsurl,callback){
var
nodeHead = document.getElementsByTagName(
'head'
)[0];
var
nodeScript =
null
;
if
(document.getElementById(sid) ==
null
){
nodeScript = document.createElement(
'script'
);
nodeScript.setAttribute(
'type'
,
'text/javascript'
);
nodeScript.setAttribute(
'src'
, jsurl);
nodeScript.setAttribute(
'id'
,sid);
if
(callback !=
null
) {
nodeScript.onload = nodeScript.onreadystatechange =
function
(){
if
(nodeScript.ready) {
return
false
;
}
if
(!nodeScript.readyState || nodeScript.readyState ==
"loaded"
|| nodeScript.readyState ==
'complete'
) {
nodeScript.ready =
true
;
callback();
}
};
}
nodeHead.appendChild(nodeScript);
}
else
{
if
(callback !=
null
){
callback();
}
}
}
</script>
</head>
<body>
<input type=
"button"
value=
"測試按鈕"
onclick=
"init()"
/>
</body>
</html>
|
這種方式全部瀏覽器都支持,可是後面的代碼必須放在回調函數裏面,也就是異步加載了。看需求使用把!我仍是比較喜歡第六種方法的。若是是異步加載的話,方法還有好幾種,不過個人出發點是但願實現同步加載,這裏就不對異步加載作總結了。