当将所有JavaScript文件合并成一个文件时,JavaScript文件的顺序是否重要?

32
在当今的现代时代,许多(流行的)JavaScript文件都是通过外部和本地加载的。当所有本地文件都合并(缩小)到一个文件中时,调用JavaScript文件的顺序是否特别重要?此外,许多人声称JavaScript应该放在页面底部,而其他人则说JavaScript最好留在头部。应该在何时采取哪种方法?谢谢!
google cdn latest jquery js              | external
another cdn loaded javascript js         | external

TabScript  ...js          \
GalleryLightbox  ...js     \
JavascriptMenu  ...js       \
HTMlFormsBeautifier ...js    > all minified and combined into one .js file!
TextFieldResize  ...js      /
SWFObjects  ...js          /
Tooltips ...js            /
CallFunctions   ...js    /


4
根据我的经验,将 JavaScript 放在 head 标签中更易于维护。脚本加载的顺序似乎不是很重要。我读过在 body 中使用 JS 可以更快的说法,我也认同这一点 :) 但如果我无法保证客户的正常运行时间,那么多出来的毫秒是没有价值的。如果开发人员花费更多的时间担心一个更改会因依赖项而破坏 JavaScript,并且较少解决实际业务问题,那么进展就会缓慢。话虽如此,我认为没有对错之分,但我个人偏好于将 JS 放在 head 中。 - jamesmortensen
5个回答

30

以下情况可能需要注意脚本的顺序:

  • 当一个脚本依赖于另一个脚本时。
  • 如果脚本在BODY中而不是HEAD中。 更新: 在HEAD和BODY中似乎没有区别。顺序很重要。无论如何。
  • 当你在全局命名空间中运行需要依赖于另一个脚本的代码时。

避免这些问题的最好方法是确保全局命名空间中的代码位于$(document).ready()包装器内。全局命名空间中的代码必须按照执行代码必须先定义的顺序加载。

在Firebug或Chrome Debugger中检查JavaScript错误控制台可能会告诉您脚本中出了什么问题,并让您知道需要修改什么以适应新的设置。

如果基于事件调用函数(例如页面加载、单击、插入或删除节点等),则通常不需要考虑顺序。但是,如果在全局命名空间之外进行函数调用,那就会出现问题。请考虑以下代码:

JS文件:mySourceContainingEvilFunctionDef.js

function evilGlobalFunctionCall() {
    alert("I will cause problems because the HTML page is trying to call " +
      "me before it knows I exist...  It doesn't know I exist, sniff :(  ");
}

HTML:

    <script>
        evilGlobalFunctionCall();  // JS Error - syntax error 
    </script>
    <!-- Takes time to load -->
    <script type="text/javascript" src="mySourceContainingEvilFunctionDef.js"></script>
...

无论如何,以上的提示将有助于防止这些类型的问题。


顺便提一下,您可能需要考虑利用浏览器的异步特性来拉取资源,从而获得某些速度优势。 Web浏览器最多可以同时打开4个异步连接,这意味着您的一个巨大脚本可能需要比分成块的同一脚本更长时间才能加载!还有Yahoo Research表明,合并脚本会产生更快的结果,因此结果因情况而异。

由于打开和关闭多个HTTP连接所需的时间与限制自己只使用单个连接而不是多个异步连接所花费的时间之间存在平衡,因此您可能需要在自己的端上进行一些测试以验证哪种方法在您的情况下最有效。也许打开所有连接所需的时间被浏览器异步下载所有脚本并超过打开/关闭连接的延迟所抵消。

话虽如此,在大多数情况下,合并脚本很可能会带来最快的速度增益,并被认为是最佳实践。


@Andrew - 是的,我在我的答案中指出了这一点。但同样也有可能结果会有所不同,同样的文档建议将脚本组合起来,也建议将内容分散到多个域中,以利用浏览器的异步特性和4个连接限制。对于每个团队来说,确定哪种方法更快取决于他们所处的具体情况。由于组合被认为是最佳实践,因此很可能会更好,但我不认为这是保证的。 - jamesmortensen
@jmort 避免这些问题的最佳方法是将代码加载到HEAD部分,以确保在运行之前加载所有JavaScript代码 你能详细解释一下吗?与将它们放在BODY中相比,将SCRIPT元素放在HEAD中的优势是什么? - Šime Vidas
@jmort,你是说在BODY中SCRIPT元素的顺序很重要,但在HEAD中SCRIPT元素的顺序就不那么重要了,对吗? - Šime Vidas
如果我把JS放在body中,那些脚本会在加载完成后立即运行,这意味着顺序很重要。但是在head中,我可以做一些疯狂的事情,比如将JQuery验证器JS放在JQuery核心脚本导入之前,而且不会有影响。在body中,验证器代码会加载并尝试运行,然后抱怨$是语法错误。 - jamesmortensen
阅读评论和答案后,我想补充一下使用 defertype=module 作为确保脚本有序执行的更好方式。这样做完全消除了在 HTML 中定位 JS 及其依赖关系的问题,即使出于某些原因,您更喜欢将JS嵌入HTML内部也是如此。 - odus-ex
显示剩余5条评论

8

是的,这取决于您做什么。

例如,如果 a.js 包含...

var a = function() {
   alert('a');
}

...而b.js则...

a()

如果您不想让a.js中的a()函数无法使用,那么您就不应该在a.js之前包含b.js

这仅适用于函数表达式;声明将提升到其作用域的顶部。

至于是否应该合并jQuery,我认为最好使用Google托管的副本-将其添加到您的组合文件中将使其变得更大,而客户端已经缓存该文件的可能性很大。


4

阅读来自 Webkit 团队的这篇文章,了解浏览器如何加载和执行脚本文件的有价值信息。

通常情况下,当解析器遇到外部脚本时,解析会暂停,发出下载脚本的请求,并且只有在脚本完全下载并执行后,解析才会恢复

因此,在没有使用异步或延迟属性的情况下,脚本按照它们在源代码中指定的顺序被执行。但是,如果脚本标签位于 <head> 中,浏览器将在开始执行任何内容之前等待所有脚本加载完成

这意味着将脚本拆分为多个文件与否并没有区别。


1
如果我理解你的问题,我认为你在问一个函数/方法定义在文件中的位置是否重要,答案是不重要,你可以在单个源文件的任何位置定义它们。JavaScript 解析器将在尝试运行代码之前读取所有符号。

1
如果您有两个文件定义了同名变量或函数,它们被包含的顺序将会改变哪一个实际上被定义。

你把它们合并到同一个文件中了吗? - Andrew Noyes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接