包含相同的JavaScript库会带来什么危险?

46

我正在开发的一个web应用由许多部分HTML文件组成。如果该部分需要JavaScript库,例如YUI,则该部分将包含YUI库。

当这些部分在运行时合并时,生成的HTML通常会多次包含YUI库。

<html>
... 
<script type="text/javascript" src="/js/yahoo/yahoo-min.js"></script>
... 
<script type="text/javascript" src="/js/yahoo/yahoo-min.js"></script>
... 
<script type="text/javascript" src="/js/yahoo/yahoo-min.js"></script>
...
</html>

我曾多次看到包含jQuery可能会出现奇怪的行为,特别是在使用AJAX时。为什么重复包含相同的JavaScript库是个坏主意?为什么它只有在某些情况下才会引起问题?


39
最大的风险是它会导致浏览器在客户机中形成奇点,并最终演变成吞噬整个星球的黑洞......所以请不要这样做。 ;) - FrustratedWithFormsDesigner
3
如果你把那个作为答案,我会给你点赞。两次。 - Trufa
1
如果他把那个作为答案,我会给他点赞42次。 - Mawg says reinstate Monica
4个回答

37

根据库的不同,多次引用可能会产生不良影响。

可以这样想,如果您有一个脚本将单击事件与按钮绑定,并且您两次包含了该脚本,那么当单击按钮时,这些操作将运行两次。

您可以编写一个简单的函数来调用以加载脚本,并使其跟踪已经加载的文件。或者,您可以使用现有的 JS 加载器,例如 LabJS 并进行修改。


4
重点在于“可重入性(re-entrancy)”。现在涉及的是“模块加载”而不是函数执行。设计者是否做了一些(愚蠢的)事情,导致第二次包含破坏了逻辑?通常情况下不会这样,但可以想象某些场景会发生这种情况——特别是每次运行都依赖于某些外部状态且逻辑应用不正确的情况下。 - user166390

11

你应该采用我在查看 HTML5 Boilerplate 源代码时学到的一种方法:

<script>
    !window.YAHOO && document.write(
        unescape('%3Cscript src="/js/yahoo/yahoo-min.js"%3E%3C/script%3E')
    );
</script>

我不使用YUI,所以将!window.YAHOO替换为YUI使用的任何全局对象。

这种方法只会在全局范围内不存在库时才加载它。


3
有不使用 document.write 的方法来实现这一点,因为它存在各种问题。 HTML5模板不再使用这段代码。 - Flimm

8
浏览器会缓存文件,只下载一次。但是它将被执行多次。因此,性能影响可以忽略不计,但正确性的影响可能不会。

13
它会对其进行多次评估。 - ide
1
在一瞬间,然而微不足道。 - Julio Santos
3
朱利奥,那个评论没有任何意义。 - Stephen
1
不一定。可能需要重新构建所有对象,并导致大量垃圾回收。这可能需要几十毫秒的时间——导致页面加载出现明显的延迟。 - Martijn
9
评估可能并不昂贵(以时间或在这种情况下,心跳计算),但可能会导致您的代码无法按预期工作。例如,加载jQuery,然后加载jQuery UI,接着是一些自定义的jQuery UI小部件。然后再次加载jQuery UI,糟糕了!您的自定义小部件就不见了! - Zack The Human
加载库可能会产生严重的副作用,但这取决于它的编写方式。例如,在全局范围内覆盖对象/数据。但是脚本可以检查它自己的全局名称是否已经存在,并且不再执行。 - kashiraja

7

每次它被执行。根据文件的不同,这可能是不可取的。例如,如果文件包含 alert("hello"),则每次引用文件时都会显示警告框,而您可能只想它仅显示一次。

编写安全库:

以下是解决方法,如果您只需要执行一次。假设您正在编写一个名为foobar.js的库,其中导出了一个全局变量 foobar:

function foobar() {
 // ...
}

您可以使用以下代码,确保文件的重复执行变成no-ops:

window.foobar = window.foobar || function foobar() {
  // ...
};

一种更现代的方法是使用JavaScript模块。JavaScript模块只会被执行一次:

<script type="module" src="example.js"></script>
<script type="module" src="example.js"></script>
<!-- example.js will only be executed once -->

使用不安全的库:

如果你不能修改库的源代码,并且已经确定此库是不安全的,那么可以通过仅加载该库一次来解决问题。创建一个全局变量,并在每次检查脚本是否已被加载时使用它。你需要自己将全局变量设置为true。

<script>(function() {
  if (! window.foobar) {
    var script = document.createElement('script');
    script.async = true; // remove this if you don't want the script to be async
    script.src = 'foobar.js';
    document.getElementsByTagName('head')[0].appendChild(script);
    window.foobar = true;
 }
}();</script>

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