RequireJS в расширении Chrome

Я создаю расширение Chrome, которое добавляет некоторые JavaScript в статьи Википедии. Насколько я знаю, единственный способ использовать RequireJS - добавить строку

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

Однако в моем расширении Chrome у меня нет доступа к HTML, чтобы добавить эту строку. Любые предложения?

Ответы

Ответ 1

У вас есть доступ к DOM страницы из расширения Chrome через контент script, однако содержимое script будет имеют доступ только к объектам JavaScript, созданным самим содержанием script.

Существует множество способов включения скриптов из расширения Chrome, как вы его включите, будет основано на том, что вы планируете с ним делать.

Если вы хотите его на всплывающей странице browser или действие, вы можете включить его из манифеста в качестве содержимого script или ссылаться на него с помощью тега script в 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>

Если вы хотите его на справочной странице, вы можете ссылаться на него с фоновой страницы с помощью тега script из относительного ресурса в вашем плагин.

Из background.html

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

Если вы хотите, чтобы он был включен в самую страницу браузера, вам нужно использовать динамическую инъекцию script на странице DOM. У вас есть доступ к странице DOM из контента script. Обратите внимание: если вы загружаете JavaScript, используя этот метод, вы плагин JavaScript (с фоновой страницы, содержимого script или всплывающего окна script) не будет иметь к нему доступа.

Вы можете либо загрузить requirejs из своего расширения, используя метод chrome.extension.getURL, либо из размещенного места в Интернете.

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)

Ответ 2

Вот как вы можете сделать это на странице фона.

В 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>

Посмотрите этот вопрос - Использование Require.js без data-main

Вы также можете сделать то же самое на странице содержимого, используя этот принцип.

Хороший вопрос, кстати. Мне понравились ответы Адама Айреса.

Ответ 3

РЕДАКТ, приведенный ниже, по-прежнему справедлив для простой ванили require.js, но нашел обходное решение путем разметки RequireJS

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

ORIGNAL POST

Ввод его в контент script является самым сложным. И приведенные выше ответы являются неполными или неверными.

Фон и всплывающее окно:
Пойдите с более ранним ответом @nafis, они будут работать

Содержание Script
Это очень сложно, и эта часть из api description является ключевой:

Условия выполнения
Скрипты контента выполняются в специальной среде, называемой изолированным миром. Они имеют доступ к DOM страницы, на которую они вставляются, но не на любые переменные или функции JavaScript, созданные на этой странице. Он просматривает каждый контент script, как будто на странице, на которой он запущен, нет другого исполняемого кода 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 начнет загружать все ваши зависимости, введя теги <script> в заголовок. Эти теги script выполняются в среде PAGE, а не в специальной среде EXTENSION. И это имеет значение.

  • Зависимости не загружаются, так как requirejs не загружается на страницу окружающая среда
  • Если владелец веб-сайта добавляет дополнительные требования, он сталкивается с конфликтом
  • Вы можете принять инъекцию 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});
});

Ответ 4

Чтобы использовать его в контенте script, вы можете просто включить его в manifest.json в качестве своей первой зависимости:

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

Он не будет загрязнять глобальное пространство имен (окно), поэтому не стоит беспокоиться об этом; его доступно только для сценариев расширения.

Также в manifest.json вы должны указать файлы, которые requirejs смогут использовать, называя их как "web_accessible_resources", чтобы упростить их перенос в папку (например, js/), чтобы вы могли использовать подстановочный знак

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

И затем в script (например, myscript.js), чтобы использовать requirejs, просто запрашивайте зависимости с помощью chrome.extension.getURL, например:

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