是否应该订购动态脚本?

时间:2010-05-10 16:02:48

标签: javascript dynamic-script-loading

我在页面加载后动态地向head元素添加了一些<script>标记。我理解脚本是异步加载的,但是我可以期望它们按照添加的顺序进行解析吗?

我在Firefox中看到了预期的行为,但在Safari或Chrome中却没有。查看Chrome开发人员工具和Firebug中的文档,两者都显示以下内容 -

<html>
  <head>
    ...
    <script type="text/javascript" src="A.js"></script>
    <script type="text/javascript" src="B.js"></script>
  </head>
  ...
</html>

然而,在查看资源加载视图时,chrome似乎会解析从服务器首先返回的任何一个,而firebug总是按照添加脚本标记的顺序加载它们,即使首先从服务器返回B也是如此。

我是否希望Chrome / Safari按指定顺序解析文件?在OS X 10.6.3上使用Chrome 5.0.375.29 beta

编辑(10/5/10):当我说解析时,我的意思是执行 - 可以看到积极解析的许多好处 - thx rikh

编辑(11/5/10):好的,所以我按照下面的juandopazo的说法进行了测试。但是我添加了一些组合,包括

  1. 使用javascript直接将脚本元素添加到头部。 (测试A - > D)
  2. 使用jquery的append()方法将脚本元素添加到头部。 (测试E - > H)
  3. 使用jquery的getScript()方法'加载'脚本。 (测试I - &gt; L)
  4. 我还尝试了脚本标签上'async'和'defer'属性的所有组合。

    您可以在此处访问测试 - http://dyn-script-load.appspot.com/,并查看来源以查看其工作原理。加载的脚本只需调用update()函数。

    首先要注意的是,只有上述第一和第三种方法并行运行 - 第二种方法依次执行请求。你可以在这里看到这个图表 -

    图1 - 请求生命周期图 Request lifecycle Graph http://dyn-script-load.appspot.com/images/dynScriptGraph.png

    有趣的是,jquery append()方法也阻止了getScript()调用 - 你可以看到它们都没有执行,直到所有append()调用完成,然后它们都并行运行。关于这一点的最后一点是,jQuery append()方法在执行后显然会从文档头中删除脚本标记。只有第一种方法会在文档中保留脚本标记。

    Chrome结果

    结果是Chrome始终执行第一个要返回的脚本,无论测试如何。这意味着所有测试'失败',除了jQuery append()方法。

    图片2 - Chrome 5.0.375.29测试版结果
    Chrome Results http://dyn-script-load.appspot.com/images/chromeDynScript.png

    Firefox搜索结果

    然而,在firefox上,如果使用第一个方法,并且async为false(即未设置),那么脚本将按顺序可靠地执行。

    图3 - FF 3.6.3结果
    FF Results http://dyn-script-load.appspot.com/images/ffDynScript.png

    请注意,Safari似乎以与Chrome相同的方式提供不同的结果,这是有道理的。

    另外,我在慢速脚本上只有500毫秒的延迟,只是为了保持开始 - >完成时间缩短。您可能需要刷新几次才能看到Chrome和Safari在所有内容上都失败。

    在我看来,如果没有这样做的方法,我们就没有利用并行检索数据的能力,也没有理由不这样做(如firefox所示)。

6 个答案:

答案 0 :(得分:2)

很抱歉回答我自己的问题,但已经有一段时间了,我们确实想出了一个解决方案。我们想出的是将javascript作为json对象中包含的文本同时加载,然后在加载它们之后使用eval()以正确的顺序执行它们。并发加载加有序执行。根据您的使用情况,您可能不需要json。粗略地说,这里有一些代码显示我们做了什么 -

// 'requests' is an array of url's to javascript resources
var loadCounter = requests.length;
var results = {};

for(var i = 0; i < requests.length; i++) {
   $.getJSON(requests[i], function(result) {
      results[result.id] = result;
      ...
      if(--loadCounter == 0) finish();
   });
}

function finish() {
  // This is not ordered - modify the algorithm to reflect the order you want
  for(var resultId in results) eval(results[resultId].jsString);
}

答案 1 :(得分:0)

根据我的理解,它们应按照它们在文档中出现的顺序执行。某些浏览器可能无法按顺序执行某些解析,但它们仍然必须以正确的顺序执行。

答案 2 :(得分:0)

不,你不能指望所有浏览器都会推迟两个脚本的执行,直到两个脚本都被加载(**尤其是当你动态添加它们时)。

如果您只想在加载B.js后才在A.js执行代码,那么最好的办法是向onload添加一个A.js回调设置变量和另一个变量一到B.js检查是否已设置该变量,然后它在B.js中执行必要的功能(如果有)(并且如果A.js未加载,则启动一个计时器,定期检查,直到它已加载。)

答案 3 :(得分:0)

下载顺序和执行顺序不一样。在您的页面中,即使首先下载了B.js,浏览器的引擎也会等待A.js继续处理页面。

脚本肯定是按照它们在文档中出现的顺序处理的,而且也是在它们出现的位置处理的。

想象一下,如果不是这样的话,如果在jQuery库之前下载并处理了使用jQuery的小脚本,那么会有很多错误。

此外,当您在js文件中执行“document.write”时,它将显示在已声明脚本的位置。您无法访问在脚本声明之后出现的DOM对象。

这就是为什么有建议将脚本放在页面的最底部,以防止它们过早执行并减少页面的“感知加载时间”,因为浏览器的渲染引擎一旦脚本停止处理完毕。

麦克

编辑:如果使用javascript动态添加它们,我认为它们会按照及时添加的顺序进行处理。

答案 4 :(得分:0)

您可以从 a.js 加载 b.js 以100%确定...虽然我想自己明确回答这个问题,特别是同步ajax加载脚本。

答案 5 :(得分:0)

我正在研究一个像YUI 3那样动态加载模块的小库。我在这里创建了一个小测试,它加载了两个只将内容插入div的脚本。一个是常见的JS文件,另一个是等待3秒执行的PHP文件。

http://www.juandopazo.com.ar/tests/asyn-script-test.html

正如您所看到的,脚本在完成加载时执行,而不是按照每个浏览器中将它们附加到DOM的顺序执行。