Почему в 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()
основанные модули.