JavaScript:如何异步下载JS?

时间:2010-05-10 14:18:40

标签: javascript google-maps asynchronous

On my web site,我正在努力尽可能地完成最快的页面加载。

我注意到我的JavaScript似乎没有异步加载。图片链接如下。

alt text http://img249.imageshack.us/img249/2452/jsasynch2.png

我的网站如何运作是需要加载两个外部JavaScript文件:

  • Google Maps v3 JavaScript和
  • JQuery JavaScript

然后,我在HTML中使用内联JavaScript,在下载上述两个文件之前无法执行。

一旦加载了这些外部javascript文件,它就会,然后才能动态呈现页面。在加载Google Maps和JQuery之前我的页面无法加载的原因是 - 我的页面基于用户的地理位置(使用Gmaps)将根据它们所在的位置显示页面(例如纽约,旧金山等)。这意味着,观看我网站的不同城市的两个人将看到不同的前页。

问题:如何让我的JavaScript文件异步下载,以便我的整个页面加载时间最快?

更新

如果我以某种方式异步下载谷歌地图和JQuery,我将如何创建一个事件,一旦 Google-maps和JQuery已经下载,就会被解雇,因为我的页面很难依赖于那些要执行的文件。

更新2

即使下面有3个答案,但实际上没有人能回答我的问题。任何帮助将不胜感激。

8 个答案:

答案 0 :(得分:7)

HTTP下载通常受到浏览器的限制,每个域每次同时下载。这就是某些网站在www.domain.tlathe images and javascript on static.domain.tla上拥有动态内容的原因。

浏览器与脚本while a script is downloading, however, the browser won't start any other downloads, even on different hostnames.

的行为略有不同

标准解决方案是将脚本移动到页面底部,但有一种解决方法可能适用于您,也可能不适合您:Insert the script DOM element using Javascript

答案 1 :(得分:4)

你可以使用这样的东西,它在大多数浏览器中运行得很好。它至少在IE6中存在一些问题,但我没有时间对它们进行调查。

var require = function (scripts, loadCallback) {
    var length        = scripts.length;
    var first         = document.getElementsByTagName("script")[0];
    var parentNode    = first.parentNode;
    var loadedScripts = 0;
    var script;

    for (var i=0; i<length; i++) {
        script = document.createElement("script");
        script.async = true;
        script.type = "text/javascript";
        script.src = scripts[i];

        script.onload = function () {
            loadedScripts++;

            if (loadedScripts === length) {
                loadCallback();
            }
        };

        script.onreadystatechange = function () {
            if (script.readyState === "complete") {
                loadedScripts++;

                if (loadedScripts === length) {
                    loadCallback();
                }
            }
        };

        parentNode.insertBefore(script, first);
    }
};


require([
    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",
    "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js",
    "http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader-min.js"
], function () {
    console.log(jQuery);
    console.log($);
    console.log(YAHOO);
});

答案 2 :(得分:4)

有人让我对这个帖子发表评论,但那是在@lonut发布回复之前。 @ lonut的代码是一个非常好的解决方案,但我有一些评论(关键且不那么关键):

首先,@ lonut的代码假定脚本没有其他脚本的加载依赖性。这有点难以解释,所以让我们使用jquery.min.js和prototype.js的简单示例。假设我们有一个简单的页面,只需加载这两个脚本:

<script src="jquery.min.js"></script>
<script src="prototype.js"></script>

请记住 - 页面中没有其他内容 - 没有其他JavaScript代码。如果您加载该页面,则下载两个脚本,一切都很好。现在,如果删除jquery.min.js脚本会发生什么?如果你从prototype.js得到错误,因为它试图引用jquery.min.js中定义的符号,那么prototype.js对jquery.min.js有一个加载依赖 - 你不能加载prototype.js,除非jquery.min.js有已经装好了。但是,如果您没有收到任何错误,则可以按照您希望的任何顺序加载这两个脚本。假设您的外部脚本之间没有负载依赖关系,@ lonut的代码非常棒。如果你确实有负载依赖 - 它会非常困难,你应该阅读更快的网站中的第4章。

第二,@ lonut代码的一个问题是Opera的某些版本将调用loadCallback两次(一次来自onload处理程序,另一次来自onreadystatechange处理程序)。只需添加一个标志,以确保只调用一次loadCallback。

第三,今天大多数浏览器每个主机名打开2个以上的连接。请参阅Roundup on Parallel Connections

答案 3 :(得分:2)

LABjs动态脚本加载器专门针对此类案例而设计。例如,你可以这样做:

$LAB
  .script("googlemaps.js")
  .script("jquery.js")
  .wait(function(){
     // yay, both googlemaps and jquery have been loaded, so do something!
  });

如果情况稍微复杂一点,并且你有一些相互依赖的脚本,正如史蒂夫·索德斯所提到的那样,你可能会这样做:

$LAB
  .script("jquery.js")
  .wait() // make sure jquery is executed first
  .script("plugin.jquery.js")
  .script("googlemaps.js")
  .wait(function(){
     // all scripts are ready to go!
  });

在任何一种情况下,LABjs将下载所有脚本(“jquery.js”,“googlemaps.js”和“plugin.jquery.js”)并行,至少到浏览器允许的程度。但是通过明智地使用链中的.wait(),LABjs将确保它们以正确的顺序执行。也就是说,如果链中的两个脚本之间没有.wait(),它们将分别执行ASAP(意味着tehm之间的不确定顺序)。如果链中的两个脚本之间存在.wait(),则第一个脚本将在第二个脚本之前执行,即使它们是并行加载的。

答案 4 :(得分:2)

以下是我如何设法在jquery mobile上异步加载gmaps: 首先,您可以加载jquery(即上面由IonuţG。Stan发布的 require 函数) 然后,您可以使用gmaps中的回调参数执行以下操作:

<!DOCTYPE html>
<html>  
<body>
 <script type="text/javascript">
      var require = function (scripts, loadCallback) {
      var length        = scripts.length;
      var first         = document.getElementsByTagName("script")[0];
      var parentNode    = first.parentNode;
      var loadedScripts = 0;
      var script;

      for (var i=0; i<length; i++) {
          script = document.createElement("script");
          script.async = true;
          script.type = "text/javascript";
          script.src = scripts[i];

          script.onload = function () {
              loadedScripts++;

              if (loadedScripts === length) {
                  loadCallback();
              }
          };

          script.onreadystatechange = function () {
              if (script.readyState === "complete") {
                  loadedScripts++;

                  if (loadedScripts === length) {
                      loadCallback();
                  }
              }
          };
          parentNode.insertBefore(script, first);
        }
       };

       require([
      "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",], function () {

         $.ajax({
            type: "GET",
            url: 'http://maps.googleapis.com/maps/api/js?v=3&sensor=false&callback=setMyMap',
            dataType: "script"
         });

       });

      function setMyMap() {


                       console.log('your actions here');



           var coords = new google.maps.LatLng(40.5439532,-3.6441775);
           var mOptions = {
              zoom: 8,
              center: coords,
              mapTypeId: google.maps.MapTypeId.ROADMAP
           }
           var map = new google.maps.Map(document.getElementById("gmap"), mOptions);
      }
 </script>

 <div id="gmap" style="width:299px; height:299px"></div>

</body>

重点是加载jquery async(你选择哪种方法),并在该回调上使用你的启动方法在gmaps脚本字符串的回调参数中对gmaps进行新的异步调用。

希望有所帮助

答案 5 :(得分:1)

无论他们下载的顺序是什么,脚本都应按照它们在页面上出现的顺序解析/执行(除非你使用DEFER)。

因此,您可以将两个Google地图放在首位,即THEN JQuery。然后,在你的页面正文的某个地方:

<script language="Javascript">

    function InitPage() {
       // Do stuff that relies on JQuery and Google, since this script should
       // not execute until both have already loaded.
       }

    $(InitPage);   // this won't execute until JQuery is ready

</script>

但这样做的好处是在加载页面开头时阻止其他连接,这对于页面性能来说并不是那么棒。

相反,您可以将JQuery保留在HEAD中,但使用JQuery的Javascript加载功能而不是Google JSAPI从 InitPage()函数加载Google脚本。然后在执行 回调函数时开始渲染。与上面相同,但改为使用此InitPage()函数:

  function InitPage() {
     $.getScript('Google Maps Javascript URL', function() {
        // Safe to start rendering now
        });

答案 6 :(得分:1)

将您的javascript包含(<script src="...)从HEAD元素移至BODY元素的末尾。通常,HEAD中放置的任何内容都是同步加载的,BODY中放置的任何内容都是异步加载的。对于脚本包含,这或多或少都是正确的,但是现在大多数浏览器会阻止脚本下面的所有内容,直到它被加载为止 - 因此,为什么在脚本底部包含脚本是最佳实践。

这是YUI guildline,它更详细地解释了它: http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html

这也是为什么样式表应该在头部,并且javascript应该在正文中的原因。因为我们不希望看到我们的页面从意大利面转向漂亮,而样式是异步加载的,我们不希望在页面加载时等待我们的javascript。

答案 7 :(得分:0)

您想到的目标将通过使用requireJS来实现。 RequireJS异步下载js资源。它是一个非常简单实用的库。请在这里阅读更多。 http://requirejs.org/