asynchronous加载脚本

我正在使用几个插件,自定义窗口小部件和一些JQuery的其他库。 结果我有几个.js和.css文件。 我需要为我的网站创build一个加载器,因为它需要一些时间来加载。 如果我可以在导入所有内容之前显示加载器,那将会很好:

<script type="text/javascript" src="js/jquery-1.6.2.min.js"></script> <script type="text/javascript" src="js/myFunctions.js"></script> <link type="text/css" href="css/main.css" rel="stylesheet" /> ... .... etc 

我find了几个教程,使我能够asynchronous导入JavaScript库。 例如我可以做一些事情:

  (function () { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'js/jquery-ui-1.8.16.custom.min.js'; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); })(); 

由于某种原因,当我做所有我的文件相同的事情页面不起作用。 我一直在努力尝试找出问题所在,但我找不到它。 首先,我认为这可能是因为一些JavaScriptfunction依赖于其他人。 但我用正确的顺序使用超时函数加载,当一个完成我进行下一个和页面仍然performance怪异。 例如我不能点击链接等…animation仍然工作虽然..

无论如何

这是我一直在想…我相信浏览器有一个caching,这就是为什么它需要很长时间才能第一次加载页面,并在下一次快速。 所以我想做的是用一个页面replace我的index.html页面,asynchronous加载所有这些文件。 当ajax完成加载所有这些文件redirect到我打算使用的页面。 当使用该页面时,不应该花很长时间才能加载,因为文件应该被包含在浏览器的caching中。 在我的索引页(.js和.css文件asynchronous加载的页面)我不在乎得到错误。 我将只显示一个加载器,并在完成时redirect页面…

这个想法是一个很好的select吗? 或者我应该不断尝试实现asynchronous方法?


编辑

我加载一切asynchronous的方式是这样的:

 importScripts(); function importScripts() { //import: jquery-ui-1.8.16.custom.min.js getContent("js/jquery-1.6.2.min.js",function (code) { var s = document.createElement('script'); s.type = 'text/javascript'; //s.async = true; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext1,1); }); //import: jquery-ui-1.8.16.custom.min.js function insertNext1() { getContent("js/jquery-ui-1.8.16.custom.min.js",function (code) { var s = document.createElement('script'); s.type = 'text/javascript'; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext2,1); }); } //import: jquery-ui-1.8.16.custom.css function insertNext2() { getContent("css/custom-theme/jquery-ui-1.8.16.custom.css",function (code) { var s = document.createElement('link'); s.type = 'text/css'; s.rel ="stylesheet"; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext3,1); }); } //import: main.css function insertNext3() { getContent("css/main.css",function (code) { var s = document.createElement('link'); s.type = 'text/css'; s.rel ="stylesheet"; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext4,1); }); } //import: jquery.imgpreload.min.js function insertNext4() { getContent("js/farinspace/jquery.imgpreload.min.js",function (code) { var s = document.createElement('script'); s.type = 'text/javascript'; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext5,1); }); } //import: marquee.js function insertNext5() { getContent("js/marquee.js",function (code) { var s = document.createElement('script'); s.type = 'text/javascript'; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext6,1); }); } //import: marquee.css function insertNext6() { getContent("css/marquee.css",function (code) { var s = document.createElement('link'); s.type = 'text/css'; s.rel ="stylesheet"; s.innerHTML=code; var x = document.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); setTimeout(insertNext,1); }); } function insertNext() { setTimeout(pageReadyMan,10); } } // get the content of url and pass that content to specified function function getContent( url, callBackFunction ) { // attempt to create the XMLHttpRequest and make the request try { var asyncRequest; // variable to hold XMLHttpRequest object asyncRequest = new XMLHttpRequest(); // create request object // register event handler asyncRequest.onreadystatechange = function(){ stateChange(asyncRequest, callBackFunction); } asyncRequest.open( 'GET', url, true ); // prepare the request asyncRequest.send( null ); // send the request } // end try catch ( exception ) { alert( 'Request failed.' ); } // end catch } // end function getContent // call function whith content when ready function stateChange(asyncRequest, callBackFunction) { if ( asyncRequest.readyState == 4 && asyncRequest.status == 200 ) { callBackFunction(asyncRequest.responseText); } // end if } // end function stateChange 

怪异的部分是所有风格的作品加上所有的JavaScriptfunction。 由于某种原因页面被冻结,但…

几个解决asynchronous加载的解决scheme:

 //this function will work cross-browser for loading scripts asynchronously function loadScript(src, callback) { var s, r, t; r = false; s = document.createElement('script'); s.type = 'text/javascript'; s.src = src; s.onload = s.onreadystatechange = function() { //console.log( this.readyState ); //uncomment this line to see which ready states are called. if ( !r && (!this.readyState || this.readyState == 'complete') ) { r = true; callback(); } }; t = document.getElementsByTagName('script')[0]; t.parentNode.insertBefore(s, t); } 

如果你已经在页面上有jQuery,只需使用:

$.getScript(url, successCallback) *

此外,还可能在文档加载完成之前加载/执行脚本,这意味着在事件绑定到元素之前您需要等待document.ready

没有看到代码就不可能明确地告诉你的问题是什么。

最简单的解决scheme是将所有的脚本内联在页面的底部,这样在执行时不会阻止加载HTML内容。 它也避免了必须asynchronous加载每个需要的脚本的问题。

如果您有一个特殊的交互,并不总是需要使用某种较大的脚本,那么避免在需要的时候加载该特定的脚本(延迟加载)会很有用。

加载$.getScript脚本可能不会被caching


对于任何可以使用Promise对象等现代特征的人来说, loadScript函数已经变得非常简单:

 function loadScript(src) { return new Promise(function (resolve, reject) { var s; s = document.createElement('script'); s.src = src; s.onload = resolve; s.onerror = reject; document.head.appendChild(s); }); } 

请注意,此版本不再接受callback参数,因为返回的承诺将处理callback。 以前的loadScript(src, callback)现在是loadScript(src).then(callback)

这有额外的好处,可以检测和处理故障,例如可以调用…

 loadScript(cdnSource) .catch(loadScript.bind(null, localSource)) .then(successCallback, failureCallback); 

…它会妥善处理CDN中断。

HTML5的新“asynchronous”属性应该是诀窍。 如果你关心IE浏览器,大多数浏览器也支持'延迟'。

asynchronous – HTML

 <script async src="siteScript.js" onload="myInit()"></script> 

推迟 – HTML

 <script defer src="siteScript.js" onload="myInit()"></script> 

在分析新的AdSense广告单元代码时,我注意到了这个属性,search引导我在这里: http : //davidwalsh.name/html5-async

我加载脚本asynchronous(HTML 5具有该function),当所有的脚本完成加载我redirect页面index2.html其中index2.html使用相同的库。 因为一旦页面redirect到index2.html,浏览器就拥有一个caching,index2.html在不到一秒的时间内加载,因为它只需要加载页面。 在我的index.html页面中,我还加载了我计划使用的图像,以便浏览器将这些图像放在caching中。 所以我的index.html如下所示:

 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>Project Management</title> <!-- the purpose of this page is to load all the scripts on the browsers cache so that pages can load fast from now on --> <script type="text/javascript"> function stylesheet(url) { var s = document.createElement('link'); s.type = 'text/css'; s.async = true; s.src = url; var x = document.getElementsByTagName('head')[0]; x.appendChild(s); } function script(url) { var s = document.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = url; var x = document.getElementsByTagName('head')[0]; x.appendChild(s); } //load scritps to the catche of browser (function () { stylesheet('css/custom-theme/jquery-ui-1.8.16.custom.css'); stylesheet('css/main.css'); stylesheet('css/marquee.css'); stylesheet('css/mainTable.css'); script('js/jquery-ui-1.8.16.custom.min.js'); script('js/jquery-1.6.2.min.js'); script('js/myFunctions.js'); script('js/farinspace/jquery.imgpreload.min.js'); script('js/marquee.js'); })(); </script> <script type="text/javascript"> // once the page is loaded go to index2.html window.onload = function () { document.location = "index2.html"; } </script> </head> <body> <div id="cover" style="position:fixed; left:0px; top:0px; width:100%; height:100%; background-color:Black; z-index:100;">Loading</div> <img src="images/home/background.png" /> <img src="images/home/3.png"/> <img src="images/home/6.jpg"/> <img src="images/home/4.png"/> <img src="images/home/5.png"/> <img src="images/home/8.jpg"/> <img src="images/home/9.jpg"/> <img src="images/logo.png"/> <img src="images/logo.png"/> <img src="images/theme/contentBorder.png"/> </body> </html> 

另一件好事就是我可以在页面中放置一个加载器,当页面加载完成后,加载器将消失,毫秒级的新页面将会运行。

从谷歌的例子

 <script type="text/javascript"> (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js?onload=onLoadCallback'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })(); </script> 

几个注释:

  • s.async = true对于HTML5 doctype来说不是很正确,正确的是s.async = 'async' (实际上使用true 是正确的 ,这要感谢amn在下面的注释中指出)
  • 使用超时来控制顺序不是很好,也不安全,而且你也使得加载时间大得多,等于所有超时的总和!

由于最近有一个原因是asynchronous加载文件,但是为了达到这个目的,我build议在你的例子中使用更多的function驱动方式(移除console.log用于生产:)):

 (function() { var prot = ("https:"===document.location.protocol?"https://":"http://"); var scripts = [ "path/to/first.js", "path/to/second.js", "path/to/third.js" ]; function completed() { console.log('completed'); } // FIXME: remove logs function checkStateAndCall(path, callback) { var _success = false; return function() { if (!_success && (!this.readyState || (this.readyState == 'complete'))) { _success = true; console.log(path, 'is ready'); // FIXME: remove logs callback(); } }; } function asyncLoadScripts(files) { function loadNext() { // chain element if (!files.length) completed(); var path = files.shift(); var scriptElm = document.createElement('script'); scriptElm.type = 'text/javascript'; scriptElm.async = true; scriptElm.src = prot+path; scriptElm.onload = scriptElm.onreadystatechange = \ checkStateAndCall(path, loadNext); // load next file in chain when // this one will be ready var headElm = document.head || document.getElementsByTagName('head')[0]; headElm.appendChild(scriptElm); } loadNext(); // start a chain } asyncLoadScripts(scripts); })(); 

我会完成zzzzBov的回答,检查是否存在callback,并允许传递参数:

  function loadScript(src, callback, args) { var s, r, t; r = false; s = document.createElement('script'); s.type = 'text/javascript'; s.src = src; if (typeof(callback) === 'function') { s.onload = s.onreadystatechange = function() { if (!r && (!this.readyState || this.readyState === 'complete')) { r = true; callback.apply(args); } }; }; t = document.getElementsByTagName('script')[0]; t.parent.insertBefore(s, t); } 

感谢HTML5,现在可以通过在标记中添加“asynchronous”来声明要asynchronous加载的脚本:

 <script async>...</script> 

注意:asynchronous属性仅用于外部脚本(只有在src属性存在的情况下才能使用)。

注意:有几种方法可以执行外部脚本:

  • 如果存在asynchronous:脚本与页面的其余部分asynchronous执行(脚本将在页面继续parsing时执行)
  • 如果async不存在并且存在延迟:脚本在页面完成parsing时执行
  • 如果不存在asynchronous或延迟:在浏览器继续parsing页面之前,脚本将被立即提取并执行

看到这个: http : //www.w3schools.com/tags/att_script_async.asp

你可能会发现这篇wiki文章很有趣: http : //ajaxpatterns.org/On-Demand_Javascript

它解释了如何以及何时使用这种技术。

加载页面时,如果你正在运行所有的脚本,你的脚本加载得如此之慢的一个原因就是:

 callMyFunctions(); 

代替:

 $(window).load(function() { callMyFunctions(); }); 

脚本的第二个位等待浏览器在开始执行任何脚本之前完全加载了所有的Javascript代码,使得用户看到页面加载速度更快。

如果你想通过减less加载时间来提高用户的体验,我不会去“加载屏幕”选项。 在我看来,这将是比只是加载页面更慢,更恼人的。

我build议你看看Modernizr 。 它是一个小的轻量级库,你可以asynchronous加载你的JavaScript的function,使您可以检查文件是否加载,并执行您指定的其他脚本。

这里是加载jQuery的一个例子:

 Modernizr.load([ { load: '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js', complete: function () { if ( !window.jQuery ) { Modernizr.load('js/libs/jquery-1.6.1.min.js'); } } }, { // This will wait for the fallback to load and // execute if it needs to. load: 'needs-jQuery.js' } ]); 

您可以使用LABJS或RequreJS

LABJSRequireJS这样的脚本加载器可以提高代码的速度和质量。

下面是asynchronous脚本加载的一个很好的当代解决scheme,尽pipe它只是用asynchronous错误来处理js脚本。

http://www.html5rocks.com有一篇很棒的文章 – 深入潜入脚本加载的黑暗水域 。

在考虑了许多可能的解决scheme之后,作者认为将js脚本添加到body元素的末尾是避免js脚本阻止页面呈现的最佳方法。

同时,作者为那些绝望地asynchronous加载和执行脚本的人添加了另一个好的替代解决scheme。

考虑到你有四个脚本命名为script1.js, script2.js, script3.js, script4.js那么你可以通过应用async = false来实现

 [ 'script1.js', 'script2.js', 'script3.js', 'script4.js' ].forEach(function(src) { var script = document.createElement('script'); script.src = src; script.async = false; document.head.appendChild(script); }); 

现在, Spec说 :一起下载,尽快下载。

Firefox <3.6,Opera说:我不知道这个“asynchronous”是什么,但是我只是按照添加的顺序执行通过JS添加的脚本。

Safari 5.0说:我明白“asynchronous”,但不明白与JS设置为“假”。 我会马上执行你的脚本,无论顺序如何。

IE <10表示:不知道“asynchronous”,但有一个解决方法使用“onreadystatechange”。

其他的都说:我是你的朋友,我们要按照这本书去做。

现在,与IE <10解决方法的完整代码:

 var scripts = [ 'script1.js', 'script2.js', 'script3.js', 'script4.js' ]; var src; var script; var pendingScripts = []; var firstScript = document.scripts[0]; // Watch scripts load in IE function stateChange() { // Execute as many scripts in order as we can var pendingScript; while (pendingScripts[0] && ( pendingScripts[0].readyState == 'loaded' || pendingScripts[0].readyState == 'complete' ) ) { pendingScript = pendingScripts.shift(); // avoid future loading events from this script (eg, if src changes) pendingScript.onreadystatechange = null; // can't just appendChild, old IE bug if element isn't closed firstScript.parentNode.insertBefore(pendingScript, firstScript); } } // loop through our script urls while (src = scripts.shift()) { if ('async' in firstScript) { // modern browsers script = document.createElement('script'); script.async = false; script.src = src; document.head.appendChild(script); } else if (firstScript.readyState) { // IE<10 // create a script and add it to our todo pile script = document.createElement('script'); pendingScripts.push(script); // listen for state changes script.onreadystatechange = stateChange; // must set src AFTER adding onreadystatechange listener // else we'll miss the loaded event for cached scripts script.src = src; } else { // fall back to defer document.write('<script src="' + src + '" defer></'+'script>'); } } 

那么, x.parentNode返回HEAD元素,所以你要在head标签之前插入脚本。 也许这就是问题所在。

尝试使用x.parentNode.appendChild()

看看这个https://github.com/stephen-lazarionok/async-resource-loader 。 它有一个例子,展示了如何一次加载JS,CSS和多个文件。

这里是我简单的内联解决scheme,以消除呈现阻止JavaScript:

https://github.com/mudroljub/js-async-loader

简而言之,它会asynchronous加载所有的脚本,然后执行它们。

你有没有考虑过使用Fetch Injection? 我推出了一个名为fetch-inject的开源库来处理这些情况。 下面是你的加载器可能看起来像使用lib:

 fetcInject([ 'js/jquery-1.6.2.min.js', 'js/marquee.js', 'css/marquee.css', 'css/custom-theme/jquery-ui-1.8.16.custom.css', 'css/main.css' ]).then(() => { 'js/jquery-ui-1.8.16.custom.min.js', 'js/farinspace/jquery.imgpreload.min.js' }) 

为了向后兼容,利用function检测并回退到XHR注入或脚本DOM元素,或者使用document.write将标记内嵌到页面中。

我会build议首先考虑缩小文件,看看是否给你足够的速度提升。 如果你的主机很慢,可以尝试把这个静态内容放在CDN上。