如何在同一页面上隔离不同的 javascript 库?

Posted

技术标签:

【中文标题】如何在同一页面上隔离不同的 javascript 库?【英文标题】:How to isolate different javascript libraries on the same page? 【发布时间】:2011-05-09 01:36:30 【问题描述】:

假设我们需要在第三方页面中嵌入一个小部件。例如,此小部件可能使用 jquery,因此小部件本身带有一个 jquery 库。 假设第三方页面也使用 jquery,但版本不同。 嵌入小部件时如何防止它们之间的冲突? jquery.noConflict 不是一个选项,因为它需要为页面中加载的第一个 jquery 库调用此方法,这意味着第三方网站应该调用它。这个想法是第三方网站不应该修改或做任何事情,除了将带有 src 的标签添加到小部件以便使用它。

这也不是 jquery 的问题——谷歌闭包库(甚至已编译)可以作为示例。

除了明显的 iframe 之外,还有哪些解决方案可以隔离不同的 javascript 库? 也许将 javascript 作为字符串加载,然后在匿名函数中 eval(通过使用 Function('code to eval'),而不是 eval('code to eval'))它可能会成功?

【问题讨论】:

我怎么强调"loading javascript as string and then eval it" 的想法有多糟糕,只是......不要,还有更多的理由不这样做。 这是一个有趣的问题,在每个组件平台(DLL、COM、托管代码)中都会出现。这不是 您的组件已损坏并且您需要更新它以使用最新的库 的情况。这是一个真正的问题,我想看看如何用动态语言解决它。 尼克,你是对的,但 eval 我的意思是实际上使用 Function('code to eval'),而不是 eval('code to eval') 本身。我会更新我的问题。 【参考方案1】:

您可以很好地在jQuery上调用Google API的完整URL而不是寻找没有冲突的方法,以便它可以在应用程序中工作。

【讨论】:

并非总是如此 - 应用程序可能发布在禁止访问 Google API URL 的公司 Intranet 中。 这并没有解决实际问题,即两个版本都将尝试绑定到 window.$ 和 window.jQuery. 他要求没有冲突的代码,所以我给出了这个想法。我不知道这是怎么回事 另一个坏主意,因为可以在 jquery 1.3.2 上创建一个网站,而我的插件使用 jquery 1.7.x,更改是巨大的,并且会破坏初始站点,因为大多数方法已经过时或做其他事情完全地。隔离是注入插件的唯一方法。【参考方案2】:

实际上,我认为jQuery.noConflict 正是您想要使用的。如果我正确理解了它的implementation,您的代码应该如下所示:

(function () 
var my$;

// your copy of the minified jQuery source

my$ = jQuery.noConflict(true);
// your widget code, which should use my$ instead of $
());

noConflict 的调用会将全局jQuery$ 对象恢复为其以前的值。

【讨论】:

问题是如果第三个应用程序将使用 noConflict 加载 jquery,并且该版本将与我期望使用的版本不兼容。 实际上,不,考虑到我草拟的解决方案,这不是问题。您的代码在匿名函数的范围内引用了 my$ 变量(实际上可以命名为 $)。第三个应用程序超出该范围的更改不会对其价值产生任何影响。 +1 这当然是 jQuery 处理这个问题的方式,但是重构你自己的所有代码很烦人。如果第三方脚本使用 noConflict 和 noConflict 来确保与可能使用其代码的每个人的兼容性会更好。 jQuery.noConflict(true) 中的true 通过将jQuery 变量返回到jQuery 的“原始”版本来处理这个问题,除了通常的$。见github.com/jquery/jquery/blob/… 这个答案如何解决其他库冲突的问题?【参考方案3】:

Function(...) 在你的函数中生成一个 eval ,但也好不到哪里去。

为什么不使用iframe,它们为第三方内容提供默认沙盒。

对于友好的人,您可以在他们和您的页面之间共享文本数据,对于现代浏览器使用 parent.postMessage 或对于老年人使用 window.name hack。

【讨论】:

是的,我正在这样做 - iframe 将我的 js 沙箱化。 吸框架.. boooooo。我讨厌 iframe。 jQuery.noConflict(true); 是他所需要的(用于第 3 方注入)iframe 是跨站点脚本编写的令人难以置信的头痛问题,而不是第 3 方插件创作的解决方案。 @ppumkin 这里的问题不是跨站脚本或插件,而是关于如何隔离 JS 代码。如果您有其他解决方案,请随时回答。 此外,iframe 还负责 CSS 隔离。我确实很不错的奖金。 @StijndeWitt,是的。但是在我们的应用程序中,我们在主页中加载 CSS。以模块名称为选择器添加前缀。 eg:.moduleName .fieldClass...,所以最后没有使用隔离。【参考方案4】:

我建立了一个库来解决这个问题。当然,我不确定它是否会对您有所帮助,因为代码仍然必须首先意识到问题并使用该库,因此只有在您能够更改代码以使用该库时它才会有所帮助。

有问题的库称为Packages JS,可以免费下载和使用,因为它是在知识共享许可下开源的。

它基本上是通过在函数中打包代码来工作的。从这些函数中,您可以导出要公开给其他包的对象。在消费者包中,您将这些对象导入本地命名空间。其他人甚至您自己是否多次使用相同的名称都没关系,因为您可以解决歧义。

这是一个例子:

(文件示例/greeting.js)

Package("example.greeting", function() 
  // Create a function hello...
  function hello() 
    return "Hello world!";
  ;

  // ...then export it for use by other packages
  Export(hello);

  // You need to supply a name for anonymous functions...
  Export("goodbye", function() 
    return "Goodbye cruel world!";
  );
);

(文件示例/ambiguity.js)

Package("example.ambiguity", function() 
  // functions hello and goodbye are also in example.greeting, making it ambiguous which
  // one is intended when using the unqualified name.
  function hello() 
    return "Hello ambiguity!";
  ;

  function goodbye() 
    return "Goodbye ambiguity!";
  ;

  // export for use by other packages
  Export(hello);
  Export(goodbye);
);

(文件示例/ambiguitytest.js)

Package("example.ambiguitytest", ["example.ambiguity", "example.greeting"], function(hello, log) 
  // Which hello did we get? The one from example.ambiguity or from example.greeting?
  log().info(hello());  
  // We will get the first one found, so the one from example.ambiguity in this case.

  // Use fully qualified names to resolve any ambiguities.
  var goodbye1 = Import("example.greeting.goodbye");
  var goodbye2 = Import("example.ambiguity.goodbye");
  log().info(goodbye1());
  log().info(goodbye2());
);

example/ambiguitytest.js 使用了两个库,它们都导出函数再见,但它可以显式导入正确的库并将它们分配给本地别名以消除它们之间的歧义。

以这种方式使用 jQuery 意味着通过将其代码包装在对 Package 的调用中并导出它现在公开给全局范围的对象来“打包”jQuery。这意味着稍微改变一下库,这可能不是您想要的,但是很遗憾,如果不求助于 iframe,我就无法解决这个问题。

我计划在下载中包含流行库的“打包”版本,jQuery 肯定在列表中,但目前我只有 jQuery 的选择器引擎 Sizzle 的打包版本。

【讨论】:

看起来很有趣.. 但如果有人是菜鸟并在整个地方注入插件代码形式,那就不是真的了。在大多数情况下,对于第 3 方来说,noconfilt 是最佳做法 有没有办法转译或处理第三方库? 啊,你偶然发现了我的一篇旧帖子。我自己不再使用 packages.js,因为与此同时,RequireJS 等更好的解决方案已经过时了它。既然你提到了转译:这些天我在 ES6 中编码(它有原生的 importexport),然后使用 Babel 和 Webpack 为浏览器将我的代码转译为 ES5。【参考方案5】:
<script src="myjquery.min.js"></script>
<script>window.myjQuery = window.jQuery.noConflict();</script>
...
<script src='...'></script> //another widget using an old versioned jquery
<script>
(function($)
    //...
    //now you can access your own jquery here, without conflict
)(window.myjQuery);
delete window.myjQuery;
</script>

最重要的几点:

    在您自己的 jquery 和相关插件标签之后立即调用 jQuery.noConflict() 方法

    将结果 jquery 存储到全局变量中,名称几乎不会发生冲突或混淆

    使用旧版本的 jquery 加载您的小部件;

    接下来是你的逻辑代码。为方便起见,使用闭包获取私有 $。私有 $ 不会与其他 jquery 冲突。

    您最好不要忘记删除全局临时变量。

【讨论】:

以上是关于如何在同一页面上隔离不同的 javascript 库?的主要内容,如果未能解决你的问题,请参考以下文章

如何在同一网页上有不同的视频弹出窗口?

如何在同一页面上运行不同版本的 jQuery?

如何通过单击放置在同一页面上的小部件来显示不同的小部件

在 jquery/javascript 处理后移动到命名锚点

如何从不同的上传点在同一页面上上传多个文件。使用 nodejs express-fileupload

Javascript 代码重定向到同一页面上的一个部门