Коротка відповідь
Дайте кожному контексту браузера явну точку входу, а спільним робіть лише код, який безпечно виконується в усіх споживачів. Вікно, сервісний воркер і контентний скрипт мають різні життєві цикли й доступні API. Один глобальний об’єкт застосунку приховує ці відмінності навіть у добре названій папці.
Відокремте встановлюваний пакет від дерева коду. src/worker/index.ts може перетворитися на dist/worker.js; маніфест має називати worker.js відносно dist. Браузер перевіряє завантажений пакет, а не організацію вихідних файлів. Цей матеріал стосується відповідальності та меж збирання; початковий посібник описує перше робоче розширення, а посібник із маніфесту — його окремі ключі.
Практичні кроки реалізації
Відокремте точки входу від повторно використовуваної логіки
Нехай popup/index монтує інтерфейс, worker/index реєструє слухачів, а content/index підключає поведінку сторінки. Не допускайте DOM-імпортів у залежності воркера. Розмістіть перевірки й перетворення у звичайних модулях без window, document та неявних браузерних викликів. Тоді контентний скрипт зможе використати форматувальник звіту без імпорту відображення вікна.
Розподіліть стан за тривалістю
Вибрана вкладка у вікні — стан подання. Збережені вподобання належать сховищу розширення. Початковий текст сторінки належить контексту, який його змінив, і має узгоджуватися з пізнішими змінами сайту. Передавання всіх трьох через одне універсальне сховище створює застарілу відповідальність і зайві повідомлення.
Зробіть контракт збирання видимим
Скомпілюйте всі точки входу, скопіюйте HTML і зображення, створіть або скопіюйте маніфест у єдиний вихідний корінь. Перевірте існування всіх згаданих файлів. Хешовані назви ресурсів корисні для вебзбирання, але маніфест не може їх вгадати: збережіть сталі назви точок входу або створюйте посилання з результату збирання.
Переглядайте зміни за межами
Нова можливість має разом відображатися в точці входу, дозволі маніфесту, контракті повідомлення та продуктовому поясненні. Якщо мала UI-зміна змушує кожен контекст імпортувати один браузерний адаптер, перегляньте його відповідальність. LocaleStress ілюструє конкретну продуктову відмінність: тимчасові зміни сторінки, експорт звітів і необов’язкові збережені запуски — різні обов’язки. Публічний опис функцій не доводить конкретної архітектури папок.
Приклад реалізації
Дерево коду може залишатися малим із явними межами:
src/
popup/index.ts
worker/index.ts
content/index.ts
shared/messages.ts
shared/report.ts
adapters/settings.ts
static/
popup.html
icons/
manifest.json
dist/
manifest.json
popup.html
popup.js
worker.js
content.js
icons/
Використовуйте контракт повідомлень із розрізнюваними варіантами замість назви методу й довільних аргументів для привілейованого коду:
export type Request =
| { type: "settings/read" }
| { type: "report/save"; report: { id: string; text: string } };
export function isSettingsRead(value: unknown): value is { type: "settings/read" } {
return typeof value === "object" && value !== null
&& "type" in value && value.type === "settings/read";
}
Тип допомагає довіреним викликам компілюватися; валідатор захищає середовище отримувача. Додайте перевірку кожного прийнятого повідомлення, зокрема розміру звіту та дозволених полів. Збережіть форматувальник чистим, щоб модульний тест перевіряв результат без удаваної емуляції Chrome. Спільний код копіюється або пакується в кожен контекст; імпорт не створює спільної пам’яті.
Компроміси та альтернативи
Папки функцій корисні, коли одна стала функція охоплює кілька контекстів. Зберігайте явними адаптери контексту: reports/worker-save і reports/content-collect краще пояснюють відповідальність за універсальний reports/service. Монорепозиторій може ділити чисті бібліотеки між розширеннями, але кожен продукт потребує окремого перевірюваного маніфесту й артефакту.
Пакування зменшує роботу з файлами, але може випадково втягнути DOM-бібліотеку у воркер. Перегляньте залежності точок входу й вихідні імпорти перед оптимізацією чанків. Для невеликого розширення сталі вихідні назви й прозоре копіювання можуть бути простішими за складний плагін збирання.
Поширені помилки
- Вважати singleton спільного модуля спільною пам’яттю контекстів.
- Імпортувати у воркер UI-функції, які читають
documentпід час ініціалізації модуля. - Створювати широкий диспетчер повідомлень, що дозволяє контентному скрипту вибирати привілейовані API-методи.
- Поширювати
src, тестуючиdist, або залишати шляхи маніфесту залежними від сервера розробки. - Переносити всі файли в папки функцій без пояснення контексту виконання.
Контрольний список випуску
- Кожна точка входу маніфесту існує у вихідній папці.
- Імпорти воркера не містять DOM-ініціалізації чи віддаленого виконуваного коду.
- Спільні контракти мають перевірку під час виконання в отримувача.
- Збережений і тимчасовий стан сторінки мають різних власників.
- Чисте збирання видаляє застарілі чанки й зайві скопійовані ресурси.
- ZIP випуску створюється з перевіреного вихідного кореня.
Поширені запитання
Де розміщувати обгортки браузерних API?
Поруч із контекстом, що володіє можливістю. Адаптер сховища може обслуговувати кілька сторінок розширення; адаптер DOM сторінки має залишатися на межі контентного скрипту. Діліться інтерфейсами за потреби без примусового об’єднання реалізацій в один модуль.
Коли виділяти спільний пакет?
Коли сталий чистий контракт має кількох реальних споживачів і незалежні тести. Ранній поділ папок не усуває зв’язність. Спершу приберіть браузерні глобальні об’єкти й припущення конкретної функції з повторно використовуваного коду.
Що перевіряти далі?
Простежте один запит від UI-точки входу через перевірку повідомлення, операцію сховища чи сторінки до показу результату. Потім огляньте зібрані файли цього шляху. Це виявляє архітектурні помилки надійніше за обговорення назв папок.
Первинні джерела
Пов’язані посібники та приклади
Потрібно реалізувати це у вашому продукті?
Перегляньте відповідні приклади продуктів або поділіться контекстом розробки вебзастосунку, розширення Chrome, API чи SaaS.