异步加载脚本

新手上路,请多包涵

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

 <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

我找到了几个教程,使我能够异步导入 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);
    })();

出于某种原因,当我对所有文件执行相同操作时,页面不起作用。我已经尝试了很长时间,试图找出问题所在,但我就是找不到。首先,我认为这可能是因为某些 javascript 函数依赖于其他函数。但是当一个完成时我使用超时功能以正确的顺序加载它们我继续下一个并且页面仍然表现得很奇怪。例如,我无法点击链接等…虽然动画仍然有效..

无论如何

这就是我一直在想的……我相信浏览器有缓存,这就是为什么第一次加载页面需要很长时间而下一次加载速度很快的原因。所以我想做的是用一个异步加载所有这些文件的页面替换我的 index.html 页面。当 ajax 完成加载所有这些文件时,重定向到我计划使用的页面。使用该页面时,加载时间不会很长,因为文件应该已经包含在浏览器的缓存中。在我的索引页面(.js 和 .css 文件异步加载的页面)上,我不在乎出现错误。完成后我将只显示一个加载器并重定向页面……

这个想法是一个很好的选择吗?还是我应该继续尝试实现异步方法?


编辑

我异步加载所有内容的方式就像:

 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

奇怪的是所有样式的工作加上所有的javascript函数。该页面由于某种原因被冻结…

原文由 Tono Nam 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 607
2 个回答

异步加载的几个解决方案:

 //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 才能将事件绑定到元素。

如果不查看代码,就无法具体说明您的问题是什么。

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

如果您有一个并不总是使用的特别花哨的交互,它需要某种更大的脚本,那么避免在需要之前加载该特定脚本(延迟加载)可能很有用。

* 加载了 $.getScript 的脚本可能不会被缓存


对于任何可以使用现代功能(例如 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 参数,因为返回的承诺将处理回调。以前是 loadScript(src, callback) 现在是 loadScript(src).then(callback)

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

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

…它会优雅地处理 CDN 中断。

原文由 zzzzBov 发布,翻译遵循 CC BY-SA 3.0 许可协议

HTML5 的新“异步”属性应该可以解决问题。如果您关心 IE,大多数浏览器也支持 ‘defer’。

异步 - HTML

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

推迟 - HTML

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

在分析新的 adsense 广告单元代码时,我注意到属性和搜索将我带到这里: http ://davidwalsh.name/html5-async

原文由 Donald Porter 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题