Почему в Firefox Addon импортированные объекты не могут получить доступ к импортированным модулям импортеров?

Для пояснения рассмотрим следующий упрощенный пример:

one.js

Components.utils.import('resource://gre/modules/Services.jsm');

let obj = {

  init: function() {

   Components.utils.import('chrome://myaddon/modules/two.jsm', this);
  }

  // code here has access to Services.jsm
}

two.js

this.EXPORTED_SYMBOLS = ['abc'];

this.abc = {

  // abc is imported into obj()
  // however as part of obj (), abc{} does not have access to Services.jsm
}

Я знаю, что это так, но вопрос в том, почему?
Результат таков например Services.jsm должен быть передан в каждом модуле.
Хотя Firefox кэширует модули и разница в производительности невелика, я хотел бы знать, можно ли избежать повторного импорта?

1 ответ

Решение

Как уже упоминалось @felix-kling, это связано с изоляцией на уровне модулей, и это имеет большой смысл, если вы подумаете об этом. Если бы это было не только Services будет виден другими модулями, но также abc,

Однако есть еще одна важная причина: поскольку модули кода JS запускаются один раз и кешируются после этого, что произойдет, если вы импортируете two.jsm дважды, один раз из модуля, уже импортировав Services.jsm а один раз из другого модуля не так сделал? Сейчас two.jsm "Видеть" Services будет зависеть от того, какой из других модулей был импортирован первым! Что было бы крайне противно.

В этом контексте ваш комментарий о "abc импортируется в obj()" неверен. Ваш код на самом деле импортирует abc в область верхнего уровня. Cu.import всегда будет импортировать в область верхнего уровня, если вы явно не укажете другую область для импорта.

"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // true
"abc" in obj; // false!

Если вы хотите импортировать two.jsm в obj вам нужно позвонить Cu.import со вторым аргументом.

let obj = {
  init: function() {
   Components.utils.import('chrome://myaddon/modules/two.jsm', this);
  }
};
"abc" in this; // false
"abc" in obj; // false
obj.init();
"abc" in this; // false
"abc" in obj; // true

Но это не влияет на видимость Services, конечно.

Было бы полезно, если бы Cu.import просто автоматически импортируйте некоторые модули, которые вы импортируете, такие как Services.jsm а также XPCOMUtils.jsm, Но этого не происходит и, скорее всего, никогда не произойдет из-за устаревших причин и ограничений обратной совместимости. (Например, у меня был код, который импортировал const {Promise} = Cu.import(..., {}); потому что ES6 добавил по умолчанию Promise Глобальный...; такого рода проблемы / ограничения обратной совместимости).

Альтернативы?

Ну, очевидный не использовать Cu.import для ваших собственных вещей, но используйте что-то еще. Куча дополнений, в т.ч. все дополнения SDK, конечно, имеют свой собственный стиль CommonJS require() реализация.

  • На самом деле вы можете повторно использовать загрузчик SDK, без использования SDK или с использованием только выбранных частей SDK, если хотите. Увидеть loader документация. Я знаю, что Эрик создает загрузчик в другом дополнении, не являющемся SDK Scriptish.
  • Вы можете написать свой собственный загрузчик на основе нижнего загрузчика и, возможно, Sandbox, Например, я сделал это в моем extSDK шаблон (все глобальные символы в loader.jsm == loader.jsm::exports будет виден каждому require модуль d).

Но для этого может потребоваться немало дополнительной работы, дополнительных знаний и усилий для переноса существующих модулей кода JS в require() основанные модули.

Другие вопросы по тегам