이 기사의 예에서는 js에서 스크립트를 동적으로 로드하는 방법을 설명합니다. 참고하실 수 있도록 모든 사람과 공유하세요. 자세한 내용은 다음과 같습니다.
최근에는 회사의 프런트엔드 지도 제품을 모듈로 나누어야 하는데, 사용자가 어떤 기능을 사용하는지에 따라 어떤 모듈을 로드하게 되는지에 따라 사용자 경험이 향상될 수 있기를 바랍니다.
그래서 js 동적 스크립트 로딩에 대해 연구하려고 여기저기 찾아봤는데 정말 안타깝네요! , 인터넷에 거의 같은 글이 있습니다. 4가지 방법 남의 결과를 복사하고 원본 글에 대한 링크를 추가하지 않는 사람을 싫어합니다. 왜! 요점은 마지막 방법이 여전히 약간 잘못되었다는 것입니다. 이틀 간의 연구와 정보를 바탕으로 여기서 이를 여러분과 공유하고 싶습니다.
먼저 로드할 js 파일이 필요합니다. 고정 폴더에 package.js를 만든 후 그 안에 functionOne 메서드를 작성했습니다.
function functionOne(){ alert("成功加载"); }
이후 html 파일은 모두 같은 디렉토리에 생성됩니다.
방법 1: 직접 문서.작성
다음 코드를 사용하여 동일한 폴더 아래에 function1.html을 만듭니다.
<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를 통해 동적으로 생성된 "Test Running Effect")을 클릭하면 스크립트가 로드되는 것으로 나타났습니다. 이 방법은 비동기 로딩이므로(다음 코드를 계속 실행하는 동안 추가 스레드가 열려 로드해야 하는 스크립트를 실행함) document.write가 인터페이스를 다시 작성하므로 이는 분명히 실용적이지 않습니다.
방법 2: 기존 스크립트의 src 속성을 동적으로 변경
다음 코드를 사용하여 동일한 폴더 아래에 function2.html을 만듭니다.
<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>
이 방법의 장점은 인터페이스 요소를 변경하지 않고 인터페이스 요소를 다시 작성하지 않는다는 것입니다. 그러나 이 방법도 비동기적으로 로드되므로 동일한 문제가 발생합니다.
방법 3: 스크립트 요소를 동적으로 생성(비동기)
다음 코드를 사용하여 동일한 폴더 아래에 function3.html을 만듭니다.
<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>
두 번째 방법에 비해 이 방법의 장점은 처음에 인터페이스에 스크립트 태그를 작성할 필요가 없다는 점입니다. 단점은 비동기 로딩이며 동일한 문제입니다.
이 세 가지 방법은 모두 비동기적으로 실행되므로 이 스크립트를 로드하는 동안 기본 페이지의 스크립트가 계속 실행됩니다. 위의 방법을 사용하면 다음 코드가 예상한 효과를 얻지 못합니다.
그러나 functionOne 앞에 경고를 추가하여 메인 페이지 스크립트의 실행을 차단할 수 있습니다. 그런 다음 functionOne이 실행될 수 있거나 이후 코드를 다른 버튼에서 실행해야 합니다. 또는 타이머를 정의하고 정해진 시간 후에 다음 코드를 실행합니다. 그러나 프로젝트에서 이러한 메서드를 사용하는 것은 절대 불가능합니다.
실제로 세 번째 방법은 몇 가지만 변경하면 동기 로딩으로 변경될 수 있습니다.
방법 4: 스크립트 요소를 동적으로 생성(동기식)
다음 코드를 사용하여 동일한 폴더 아래에 function4.html을 만듭니다.
<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는 3f1c4e4b6b16bbbd69b2ee476dc4f83a를 특수 요소로 취급하며 해당 하위 노드에 대한 DOM 액세스를 허용하지 않습니다. 그러나 다음 예와 같이 3f1c4e4b6b16bbbd69b2ee476dc4f83a 요소의 텍스트 속성을 사용하여 js 코드를 작성할 수 있습니다.
var myScript= document.createElement("script"); myScript.type = "text/javascript"; myScript.text = "function functionOne(){alert(\"成功运行\"); }"; document.body.appendChild(myScript); //此处可以运行 functionOne();
이러한 방식으로 수정된 코드는 IE, Firefox, Opera 및 Safari3 이상 버전에서 실행될 수 있습니다. 3.0 이전 버전의 Safari에서는 텍스트 속성을 올바르게 지원하지 않았지만 텍스트 노드 기술을 사용하여 코드를 지정할 수 있었습니다. 이전 버전의 Safari와 호환되어야 하는 경우 다음 코드를 사용할 수 있습니다.
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 속성을 사용해야 합니다. 전체 프로세스는 다음 함수로 표현될 수 있습니다.
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") 형태의 메소드를 제공하기를 원하므로 계속 논의가 필요합니다.
방법 5: XMLHttpRequest/ActiveXObject 비동기 로딩
다음 코드를 사용하여 동일한 폴더 아래에 function5.html을 만듭니다.
<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,方便以后直接调用,代码如下:
/** * 同步加载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,代码如下:
<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>
这种方式所有浏览器都支持,但是后面的代码必须放在回调函数里面,也就是异步加载了。看需求使用把!我还是比较喜欢第六种方法的。如果是异步加载的话,方法还有好几种,不过我的出发点是希望实现同步加载,这里就不对异步加载做总结了。
希望本文所述对大家JavaScript程序设计有所帮助。