js實現動態加載腳本的方法實例彙總

 

 

本文實例講述了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>

這種方式全部瀏覽器都支持,可是後面的代碼必須放在回調函數裏面,也就是異步加載了。看需求使用把!我仍是比較喜歡第六種方法的。若是是異步加載的話,方法還有好幾種,不過個人出發點是但願實現同步加載,這裏就不對異步加載作總結了。

相關文章
相關標籤/搜索