如何在Chrome扩展程序的内容脚本之间重复使用代码?

6

我正在开发一个Chrome扩展程序,其中包含许多内容脚本。其中大部分使用相同的函数,因此我希望在这些内容脚本之间共享这些函数; 但是我似乎无法弄清楚如何做到这一点。
内容脚本是沙盒化的,因此无法访问相同的window对象。看起来应该有一个显而易见的解决方案,但我还没有找到它。


它们都在同一个隔离的世界中。它们可以很好地相互交互。你一直有问题让它们正常工作吗? - Teepeemm
1
"隔离世界允许每个内容脚本在不担心与页面或其他内容脚本冲突的情况下对其JavaScript环境进行更改。" 这意味着内容脚本不共享单个隔离的世界。这符合我的发现,但我宁愿错了!" - Protector one
2
@Protectorone 这仅适用于来自不同扩展的内容脚本。如果您从一个扩展程序中注入多个脚本,则它们共享上下文。 - Xan
1个回答

16
内容脚本是被沙盒化的,因此无法访问相同的窗口对象。
这并不完全正确:尽管默认情况下内容脚本无法与其加载的页面的window对象进行交互,但它们共享"隔离世界"中隐藏的相同window对象。因此,如果将两个不同的内容脚本加载到同一个页面中,您将为两个内容脚本使用相同的window对象。
以下是一个示例,请自行尝试,并执行类似于以下操作:
  • 一个manifest.json的示例:

     {
         "name": "我的扩展",
    
         ...
    
         "content_scripts": [
             {
               "matches": ["*://*/*"],
               "js": ["one.js", "two.js"]
             }
         ]
     }
    
  • 带有以下函数的脚本one.js

     function sayHello(name) {
         alert("你好," + name + "!");
     }
    
  • 使用该函数的脚本two.js

     sayHello('约翰');
    

这里会发生什么?

很容易告诉你:

  1. 当页面加载时,内容脚本被注入
  2. one.js 脚本首先被注入,并定义了 sayHello 函数
  3. 现在 two.js 和任何在 one.js 之后被注入的其他内容脚本都可以使用该函数
  4. 调用 sayHello('John'); 将会弹出预期的 "Hello John!"。

这就是为什么大多数开发人员(包括我)喜欢做这样的事情:

"content_scripts": [
    {
      "matches": ["*://*/*"]
      "js": ["jquery.min.js", "script.js"]
    }
]

因为这是一种简单的方法,可以包含jQuery并在您的内容脚本中使用它。
显然,即使从背景页使用chrome.scripting.executeScript()(MV3)或chrome.tabs.executeScript()(MV2)函数注入,内容脚本仍将共享相同的window对象。
回答您的问题:
您可以轻松创建一个包含所有实用程序和最常用函数的脚本,这样您就可以随时在第一个脚本之后注入的任何其他内容脚本中使用它们。
所以,基本上,做这样的事情:
"content_scripts": [
    {
      "matches": ["*://*/*"]
      "js": ["script1.js", "script2.js", "script3.js", ...]
    }
]

这意味着:

  • script1.js 被注入
  • script2.js 被注入,并继承了 script1.js 的命名空间
  • script3.js 被注入,并继承了 script2.jsscript1.js 的命名空间
  • 以此类推...

澄清

当谷歌文档说

内容脚本在一个称为隔离世界的特殊环境中执行。它们可以访问被注入页面的 DOM,但不能访问页面创建的任何 JavaScript 变量或函数。对于每个内容脚本来说,它所运行的页面上似乎没有其他 JavaScript 在执行。反之亦然:在页面上运行的 JavaScript 不能调用由内容脚本定义的任何函数或访问任何变量。

这意味着

  • 内容脚本无法与其被注入到的页面的命名空间进行交互。
  • 内容脚本无法与其他扩展程序在同一页面注入的内容脚本的命名空间进行交互。

但是

  • 它们可以与页面的DOM进行交互。
  • 它们可以与同一扩展注入到同一页面上的其他内容脚本的命名空间进行交互。

在ManifestV3中,通过在其content_scripts声明中指定"world": "MAIN"(适用于Chrome 111及更高版本)或通过chrome.scripting API(适用于Chrome 95/102及更高版本)进行注入/注册,内容脚本可以加载到页面的window中。


2
澄清一下:文档的意思是“来自不同扩展的内容脚本是隔离的”。 - Xan
谢谢,我会把它加到答案中。 - Marco Bonelli
这对我来说并不起作用。我可以看到两个脚本以正确的顺序被注入和执行,但当第二个调用在第一个中定义的方法时,我会收到一个未定义的方法错误。 - gtato
已确认此方法在清单v3中同样有效。 - Alexander
我知道这是一个老问题,但是在这种情况下,你如何保持你的命名空间干净整洁? - btonasse
2
@btonasse 这取决于你。最简单的方法可能是将包装在匿名函数中的东西隔离出来,例如:(function() { /* stuff here */ })() - Marco Bonelli

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