Chrome 扩展中的 RequireJS

发布于 2024-12-29 13:12:55 字数 226 浏览 2 评论 0原文

我正在构建一个 Chrome 扩展,它将一些 JavaScript 添加到维基百科文章中。据我所知,使用 RequireJS 的唯一方法是添加行

<script data-main="scripts/bla" src="scripts/require-jquery.js>

但是,在我的 Chrome 扩展中,我无权访问 HTML 来添加此行。有什么建议吗?

I am building a Chrome extension which adds some JavaScript to Wikipedia articles. As far as I know, the only way to use RequireJS is to add the line

<script data-main="scripts/bla" src="scripts/require-jquery.js>

However, in my Chrome extension, I don't have access to the HTML to add this line. Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

时光礼记 2025-01-05 13:12:55

您确实可以通过内容脚本,但是内容脚本只能访问由内容脚本 本身。

有多种方法可以包含来自 Chrome 扩展程序的脚本,如何包含它取决于您计划使用它做什么。

如果您希望将其显示在浏览器页面操作 您可以将其从清单中作为内容脚本包含进来,也可以使用脚本标记引用它来自亲戚的 popup.html插件中的资源。

来自清单:

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["jquery.js", "myscript.js"]
    }
  ],
  ...
}

来自 popup.html:

<script data-main="scripts/bla" src="scripts/require-jquery.js>

如果您希望将其显示在后台页面中,您可以可以使用插件中相关资源的脚本标签从后台页面引用它。

来自background.html

<script data-main="scripts/bla" src="scripts/require-jquery.js>

如果您希望将其包含在浏览器页面本身中,那么您需要在页面的DOM 上使用动态脚本注入。您可以通过内容脚本访问页面的 DOM。请注意,如果您使用此技术加载 JavaScript,您的插件 JavaScript(从后台页面、内容脚本或弹出脚本)将无法访问它。

您可以使用 chrome.extension.getURL 方法从扩展程序加载 requirejs,也可以从互联网上的托管位置加载 requirejs。

var script = document.createElement('script');
script.setAttribute("type", "text/javascript");
script.setAttribute("async", true);
script.setAttribute("src", chrome.extension.getURL("require-jquery.js"));  
//Assuming your host supports both http and https
var head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
head.insertBefore(script, head.firstChild)

You do have access to the DOM of the page from the Chrome Extension via a content script, however the content script will only have access to the JavaScript objects created by the content script itself.

There are many ways to include scripts from a Chrome extension, how you include it will be based on what you plan to do with it.

If you want it in the popup page of a browser or page action you can either include it from the manifest as a content script or reference it using a script tag in the popup.html from a relative resource in your plugin.

From manifest:

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["jquery.js", "myscript.js"]
    }
  ],
  ...
}

From popup.html:

<script data-main="scripts/bla" src="scripts/require-jquery.js>

If you want it in the background page you can reference it from the background page using a script tag from a relative resource in your plugin.

From background.html

<script data-main="scripts/bla" src="scripts/require-jquery.js>

If you want it to be included in the browser page itself then you need to use dynamic script injection on the page's DOM. You have access to the page's DOM from a content script. Please note that if you load the JavaScript using this technique you plugin JavaScript (from a background page, content script or popup script) will not have access to it.

You can either load the requirejs from your extension using the chrome.extension.getURL method or from the a hosted location on the internet.

var script = document.createElement('script');
script.setAttribute("type", "text/javascript");
script.setAttribute("async", true);
script.setAttribute("src", chrome.extension.getURL("require-jquery.js"));  
//Assuming your host supports both http and https
var head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
head.insertBefore(script, head.firstChild)
御弟哥哥 2025-01-05 13:12:55

以下是在后台页面中执行此操作的方法。

manifest.json 中:

"background": {
    "scripts": [ "scripts/require.js","scripts/main.js"]
}, 

main.js 中:

require.config({
    baseUrl: "scripts"
});

require( [ /*...*/ ], function(  /*...*/ ) {
    /*...*/
});

而不是这个:

<script data-main="scripts/bla" src="scripts/require.js></script>

您应该使用这个:

<script src="scripts/require-jquery.js"></script>
<script src="scripts/main.js"></script>

请参阅这个问题 - 在没有 data-main 的情况下使用 Require.js

您也可以使用此原理在内容页面中执行相同的操作。

顺便说一句,好问题。我喜欢亚当·艾尔斯的答案。

Here is how you can do it in the background page.

In manifest.json:

"background": {
    "scripts": [ "scripts/require.js","scripts/main.js"]
}, 

In main.js:

require.config({
    baseUrl: "scripts"
});

require( [ /*...*/ ], function(  /*...*/ ) {
    /*...*/
});

Instead of this:

<script data-main="scripts/bla" src="scripts/require.js></script>

You should use this:

<script src="scripts/require-jquery.js"></script>
<script src="scripts/main.js"></script>

See this question - Using Require.js without data-main

You can also do the same thing in the content page using this principle.

Nice Question by the way. I liked Adam Ayres's answers.

毁我热情 2025-01-05 13:12:55

下面的编辑对于普通的 require.js 仍然适用,但通过分叉 RequireJS

Github 找到了解决方法:https://github。 com/jeroendelau/requirejs
Bower:bower install requirejs-for-browser-extensions

原始帖子

将其注入到内容脚本中是迄今为止最难的。并且上述答案不完整或不正确。

背景和弹出窗口:
遵循 @nafis 之前的回答,他们将使用

内容脚本
这是一个非常棘手的问题,api 描述中的这一部分是关键:

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

直觉上这应该是正确的
manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["requirejs.js", "myscript.js"]
    }
  ],
  ...
  "web_accessible_resources": [
     "js/*"
  ],
 ...
}

myscript.js

require(["myFancyModule"], function (FM) {
   ...
});

这不起作用

问题是 requirejs 将通过在标头中注入

  1. 由于页面中未加载 requirejs,依赖项无法加载
    没有
  2. 如果网站所有者已经添加了 requirejs,则可能会发生冲突
  3. 您可以决定将 require.js 注入页面,正如 @Adam 所建议的那样,但在这种情况下,您的扩展功能都
    会起作用的。存储、消息传递、跨站请求都不是
    因此

,为了使它们能够工作,需要将 requirejs 加载的模块注入到扩展环境中。可以使用 requirejs 插件来更改加载行为。

由于这种工作方式,该解决方案非常不优雅,并且它阻止您在脚本下的调试器中看到脚本。但如果你绝望的话,它会起作用。

myscript.js

/**
 * Inject the plugin straight into requirejs
 */
define("Injector", {
  load: function (name, req, onload, config) {

    //Load the script using XHR, from background
    var oReq = new XMLHttpRequest();
    oReq.addEventListener("load", function () {

      //Find depenencies in the script, and prepend the
      //Injector! plugin, forcing the load to go through this
      //plugin.
      var modified = getDeps(oReq.response)

      //have requirejs load the module from text
      //it will evaluate the define, and process dependencies
      onload.fromText(modified);
    });
    oReq.open("GET", req.toUrl(name) + ".js");
    oReq.send();

    //Find dependencies and prepend Injector!
    function getDeps(script)
    {
      //extract the define call, reduced to a single line
      var defineCall = script.match(/define([\s\S])*?{/m)[0].split("\n").join("");
      //extract dependenceis from the call
      var depsMatch = defineCall.match(/\[([\s\S]*?)\]/);

      //if there are dependencies, inject the injectors
      if (depsMatch)
      {
        var deps = depsMatch[0];
        var replaced = deps.replace(/(\'|\")([\s\S]*?)\1/g, '$1Injector!$2$1');
        return script.replace(/define([\s\S]*?)\[[\s\S]*?\]/m, 'define$1' + replaced);
      }
      //no dependencies, return script
      return script;
    }
  }
});

/**
 * Call all your dependencies using the plugin
 */
require(["Injector!myFancyModule"], function (FM) {
      chrome.storage.local.get({something});
});

EDIT below is still true for plain vanilla require.js, but found a workaround by forking RequireJS

Github: https://github.com/jeroendelau/requirejs
Bower: bower install requirejs-for-browser-extensions

ORIGNAL POST

Injecting it into the content script is by far the hardest. And the above answers are incomplete or incorrect.

Background and Popup:
Go with earlier answer by @nafis, they will work

Content Script
This is a very tricky one, and this part from the api description is key:

Execution environment
Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.

Intuitively this should be correct
manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["requirejs.js", "myscript.js"]
    }
  ],
  ...
  "web_accessible_resources": [
     "js/*"
  ],
 ...
}

myscript.js

require(["myFancyModule"], function (FM) {
   ...
});

THIS WILL NOT WORK

The problem is that requirejs will proceed to load all your dependencies by injecting <script> tags in the header. These script tags are executed in the PAGE environment, not in the special EXTENSION environment. And this matters.

  1. Dependencies fail to load since requirejs is not loaded in the page
    environment
  2. If the website owner has alreay added requirejs, it migh clash
  3. You can decide to inject require.js into the page, as @Adam suggests, but in that case none of your extension features
    will work. storage, messaging, cross-site request are all not
    available

So in order for those to work, the modules loaded by requirejs need to be injected into the extensions environment. It is possible to use a requirejs plugin to change load behavior.

Because of the way this works the solution is very inelegant, AND it prevents your from seeing the scripts in the debugger under scripts. But if you're desperate, it will works.

myscript.js

/**
 * Inject the plugin straight into requirejs
 */
define("Injector", {
  load: function (name, req, onload, config) {

    //Load the script using XHR, from background
    var oReq = new XMLHttpRequest();
    oReq.addEventListener("load", function () {

      //Find depenencies in the script, and prepend the
      //Injector! plugin, forcing the load to go through this
      //plugin.
      var modified = getDeps(oReq.response)

      //have requirejs load the module from text
      //it will evaluate the define, and process dependencies
      onload.fromText(modified);
    });
    oReq.open("GET", req.toUrl(name) + ".js");
    oReq.send();

    //Find dependencies and prepend Injector!
    function getDeps(script)
    {
      //extract the define call, reduced to a single line
      var defineCall = script.match(/define([\s\S])*?{/m)[0].split("\n").join("");
      //extract dependenceis from the call
      var depsMatch = defineCall.match(/\[([\s\S]*?)\]/);

      //if there are dependencies, inject the injectors
      if (depsMatch)
      {
        var deps = depsMatch[0];
        var replaced = deps.replace(/(\'|\")([\s\S]*?)\1/g, '$1Injector!$2$1');
        return script.replace(/define([\s\S]*?)\[[\s\S]*?\]/m, 'define$1' + replaced);
      }
      //no dependencies, return script
      return script;
    }
  }
});

/**
 * Call all your dependencies using the plugin
 */
require(["Injector!myFancyModule"], function (FM) {
      chrome.storage.local.get({something});
});
喜爱纠缠 2025-01-05 13:12:55

要在内容脚本中使用它,您只需将其作为您的第一个依赖项包含在manifest.json中即可:

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["requirejs.js", "myscript.js"]
    }
  ],
  ...
}

不会污染全局命名空间(窗口)所以不用担心;它仅适用于扩展脚本。

另外,在manifest.json中,您必须通过将它们命名为“web_accessible_resources”来命名requirejs能够使用的文件,以便轻松地将它们全部放在一个文件夹中(例如js/),以便您可以使用通配符:

{
  "name": "My extension",
  ...
  "web_accessible_resources": [
     "js/*"
  ],
  ...
}

然后在您的脚本(例如 myscript.js)中使用 requirejs 只需使用 chrome.extension.getURL 请求依赖项,如下所示:

requirejs([chrome.extension.getURL('js/library')], function(library) {
     ...
}

To use it in a content script you can just include it in the manifest.json as your first dependency:

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://www.google.com/*"],
      "css": ["mystyles.css"],
      "js": ["requirejs.js", "myscript.js"]
    }
  ],
  ...
}

It will NOT pollute the global namespace (window) so no worries about that; its only available for extension scripts.

Also in manifest.json you have to name the files that requirejs will be able to use by naming them as "web_accessible_resources", to make this easy put them all in a folder (e.g. js/) so you can use a wildcard:

{
  "name": "My extension",
  ...
  "web_accessible_resources": [
     "js/*"
  ],
  ...
}

And then in your script (e.g. myscript.js) to use requirejs just ask for the dependencies using chrome.extension.getURL, like this:

requirejs([chrome.extension.getURL('js/library')], function(library) {
     ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文