document.createElement(“脚本”)同步

是否有可能同步调用.js文件,然后立即使用它?

 <script type="text/javascript"> var head = document.getElementsByTagName('head').item(0); var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', 'http://mysite/my.js'); head.appendChild(script); myFunction(); // Fails because it hasn't loaded from my.js yet. window.onload = function() { // Works most of the time but not all of the time. // Especially if my.js injects another script that contains myFunction(). myFunction(); }; </script> 

这是简化的。 在我的实现中,createElement的东西是在一个函数中。 我想添加一些东西到function,可以检查一下,如果某个variables被实例化之前返回控制。 但是,当从另一个我无法控制的站点中joinjs时,仍然存在着怎么办的问题。

思考?

编辑:

我现在已经接受了最好的答案,因为它给出了一个很好的解释。 但如果任何人有任何build议,如何改善这一点,我向他们开放。 这是我想要做的一个例子。

 // Include() is a custom function to import js. Include('my1.js'); Include('my2.js'); myFunc1('blarg'); myFunc2('bleet'); 

我只是想不必太了解内部知识,只能说“我希望使用这个模块,现在我将使用它的一些代码”。

您可以使用“onload”处理程序创build您的<script>元素,并在脚本被浏览器加载和评估时调用。

 var script = document.createElement('script'); script.onload = function() { alert("Script loaded and ready"); }; script.src = "http://whatever.com/the/script.js"; document.getElementsByTagName('head')[0].appendChild(script); 

你不能同步。

编辑 – 有人指出,真正的forms,IE不会触发加载/评估<script>标签上的“加载”事件。 因此,我想接下来要做的是用XMLHttpRequest获取脚本,然后自己eval() 。 (或者,我猜想,将文本填充到您添加的<script>标记中; eval()的执行环境受本地范围的影响,因此它不一定会按照您希望的方式执行。

编辑截至2013年初 ,我强烈build议寻找像Requirejs更强大的脚本加载工具。 有很多特殊情况需要担心。 对于非常简单的情况, yepnope现在已经内置到Modernizr中 。

这不是很漂亮,但它的工作原理:

 <script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); </script> <script type="text/javascript"> functionFromOther(); </script> 

要么

 <script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); window.onload = function() { functionFromOther(); }; </script> 

该脚本必须包含在单独的<script>标记或window.onload()之前。

这不会工作:

 <script type="text/javascript"> document.write('<script type="text/javascript" src="other.js"></script>'); functionFromOther(); // Error </script> 

创build一个节点也是一样,正如Pointy所做的那样,但只能在FF中完成。 脚本将在其他浏览器中准备好时,您无法保证。

作为一个XML Purist我真的很讨厌这个。 但它确实有用。 你可以轻松地包装那些丑陋的document.write()所以你不必看着它们。 你甚至可以做testing并创build一个节点并追加它,然后回退到document.write()

这是晚了,但为了将来的任何人想要这样做,您可以使用以下内容:

 function require(file,callback){ var head=document.getElementsByTagName("head")[0]; var script=document.createElement('script'); script.src=file; script.type='text/javascript'; //real browsers script.onload=callback; //Internet explorer script.onreadystatechange = function() { if (this.readyState == 'complete') { callback(); } } head.appendChild(script); } 

我前段时间做过一个简短的博客文章http://crlog.info/2011/10/06/dynamically-requireinclude-a-javascript-file-into-a-page-and-be-notified-when-its -loaded /

asynchronous编程稍微复杂一点,因为发出请求的结果被封装在一个函数中,而不是遵循请求语句。 但是 用户体验的实时行为可能会更好,因为他们不会看到一个缓慢的服务器或缓慢的networking导致浏览器的行为,如果它已经崩溃。 同步编程是不尊重的不应该在人们使用的应用程序中使用。

Douglas Crockford ( YUI博客 )

好吧,扣你的座位,因为这将是一个颠簸的旅程。 越来越多的人要求通过JavaScriptdynamic加载脚本,这似乎是一个热门话题。

这个变得如此受欢迎的主要原因是:

  • 客户端模块化
  • 更容易的依赖pipe理
  • error handling
  • 性能优势

关于模块化 :显然pipe理客户端依赖性应该在客户端处理。 如果需要一个特定的对象,模块或库,我们只要求它并dynamic地加载它。

error handling :如果资源失败,我们仍然有机会阻止依赖于受影响的脚本的部分,或者甚至可能延迟尝试。

性能已经成为网站之间的竞争优势,现在是search排名的因素。 dynamic脚本可以做的是模仿asynchronous行为,而不是浏览器处理脚本的默认阻塞方式。 脚本阻止其他资源, 脚本阻止进一步parsingHTML文档, 脚本阻止 UI。 现在,使用dynamic脚本标记及其跨浏览器替代方法,您可以执行真正的asynchronous请求,并且只有在可用时才执行相关代码。 即使使用其他资源,您的脚本也会并行加载,并且渲染将是完美无瑕的。

有些人坚持使用同步脚本的原因是因为习惯了。 他们认为这是默认的方式,这是更简单的方式,有些人甚至认为这是唯一的方法。

但是,当需要决定应用程序的devise时,我们唯一应该关心的是最终用户体验 。 而在这个领域,asynchronous不能被打败。 用户得到直接的回应 (或者说承诺),一个承诺总是胜于无。 一个空白的屏幕吓人。 开发人员不应该懒惰以提高感知的性能

最后谈谈肮脏的一面。 你应该做些什么才能使它在各种浏览器上运行:

  1. 学会asynchronous思考
  2. 组织你的代码是模块化的
  3. 组织你的代码来处理错误和边界情况
  4. 逐步加强
  5. 始终照顾正确的反馈意见

上面的答案指出了我的正确方向。 这是我工作的一个通用版本:

  var script = document.createElement('script'); script.src = 'http://' + location.hostname + '/module'; script.addEventListener('load', postLoadFunction); document.head.appendChild(script); function postLoadFunction() { // add module dependent code here } 

这看起来像是一个体面的dynamic脚本加载的概述: http : //unixpapa.com/js/dyna.html

我有这个问题的现有答案(和其他stackoverflow线程上的这个问题的变化)的以下问题:

  • 没有加载的代码是可debugging的
  • 许多解决scheme需要callback来了解何时加载完成,而不是真正的阻塞,这意味着我会立即调用加载(即加载)代码得到执行错误。

或者更准确地说:

  • 没有一个加载的代码是可debugging的(除了HTML脚本标记块,当且仅当解决scheme向dom添加了脚本元素,并且从来没有作为单独的可见脚本)。 =>假设我必须加载多less个脚本和debugging),这是不能接受的。
  • 使用“onreadystatechange”或“onload”事件的解决scheme无法阻止,这是一个很大的问题,因为代码最初使用'require([filename,'dojo / domReady'])'同步加载dynamic脚本' 我正在剥离出道场。

我的最终解决scheme是在返回之前加载脚本,并且所有脚本都可以在debugging器中正确访问(至less适用于Chrome),如下所示:

警告:下面的代码应该只能在'开发'模式下使用。 (对于“释放”模式,我build议不要dynamic脚本加载,或者至less不要使用eval来预打包和缩小)。

 //Code User TODO: you must create and set your own 'noEval' variable require = function require(inFileName) { var aRequest ,aScript ,aScriptSource ; //setup the full relative filename inFileName = window.location.protocol + '//' + window.location.host + '/' + inFileName; //synchronously get the code aRequest = new XMLHttpRequest(); aRequest.open('GET', inFileName, false); aRequest.send(); //set the returned script text while adding special comment to auto include in debugger source listing: aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n'; if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!** { //create a dom element to hold the code aScript = document.createElement('script'); aScript.type = 'text/javascript'; //set the script tag text, including the debugger id at the end!! aScript.text = aScriptSource; //append the code to the dom document.getElementsByTagName('body')[0].appendChild(aScript); } else { eval(aScriptSource); } }; 

我习惯于在我的网站上有多个依赖于另一个的.js文件。 为了加载它们,并确保依赖性按照正确的顺序进行评估,我写了一个函数来加载所有的文件,然后一旦收到它们,就是eval() 。 主要的缺点是,因为这不适用于CDN。 对于这样的库(例如,jQuery),最好静态地包含它们。 请注意,在HTML中dynamic插入脚本节点并不能保证以正确的顺序对脚本进行评估,至less在Chrome中(这是编写此function的主要原因)。

 function xhrs(reqs) { var requests = [] , count = [] , callback ; callback = function (r,c,i) { return function () { if ( this.readyState == 4 ) { if (this.status != 200 ) { r[i]['resp']="" ; } else { r[i]['resp']= this.responseText ; } c[0] = c[0] - 1 ; if ( c[0] == 0 ) { for ( var j = 0 ; j < r.length ; j++ ) { eval(r[j]['resp']) ; } } } } } ; if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) { requests.length = reqs.length ; } else { requests.length = 1 ; reqs = [].concat(reqs); } count[0] = requests.length ; for ( var i = 0 ; i < requests.length ; i++ ) { requests[i] = {} ; requests[i]['xhr'] = new XMLHttpRequest () ; requests[i]['xhr'].open('GET', reqs[i]) ; requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ; requests[i]['xhr'].send(null); } } 

我还没有想出如何在不创build数组(count)的情况下引用相同的值。 否则,我认为这是不言自明的(当一切都加载, eval()每个文件中给出的顺序,否则只是存储响应)。

用法示例:

 xhrs( [ root + '/global.js' , window.location.href + 'config.js' , root + '/js/lib/details.polyfill.min.js', root + '/js/scripts/address.js' , root + '/js/scripts/tableofcontents.js' ]) ; 
 function include(file){ return new Promise(function(resolve, reject){ var script = document.createElement('script'); script.src = file; script.type ='text/javascript'; script.defer = true; document.getElementsByTagName('head').item(0).appendChild(script); script.onload = function(){ resolve() } script.onerror = function(){ reject() } }) /*I HAVE MODIFIED THIS TO BE PROMISE-BASED HOW TO USE THIS FUNCTION include('js/somefile.js').then(function(){ console.log('loaded'); },function(){ console.log('not loaded'); }) */ } 

讽刺的是,我有你想要的东西,但想要更接近你的东西。

我加载dynamic和asynchronous的东西,但像这样的loadcallback(使用dojo和xmlhtpprequest)

  dojo.xhrGet({ url: 'getCode.php', handleAs: "javascript", content : { module : 'my.js' }, load: function() { myFunc1('blarg'); }, error: function(errorMessage) { console.error(errorMessage); } }); 

有关更详细的解释,请参阅此处

问题在于代码被取消了,如果你的代码有什么问题, console.error(errorMessage); 语句将指示eval()所在的行,而不是实际的错误。 这是一个很大的问题,我实际上正在尝试转换回<script>语句(请参阅这里 。