Головна / Статті

Створіть бізнес з розширенням Manifest V3 для Chrome, який зможе вижити після призупинення

Розробляйте вікна MV3, скрипти контенту та тимчасових сервісних працівників із використанням chrome.storage для обміну повідомленнями, прав на роботу з боку сервера та дозволів, сумісних із магазином.

Видавець: REACTAPP.TOP6 хв читання1460 слів

Джерела: Building a Chrome Extension Business: Architecture, APIs, and Hard Lessons

Сторінка призначена для SaaS може вимагати місяців роботи над доменами, базами даних та механізмами автентифікації, перш ніж з’явиться десять користувачів. Розширення браузера змінюють цю ситуацію. Chrome Web Store вже функціонує як магазин додатків: люди шукають інструменти для усунення проблем у GitHub, LinkedIn чи Gmail, і корисний запис може досягти перших тисяч активних користувачів майже без необхідності оплачуваного просування.

Випуск справжнього продукту через цей канал все ще вимагає більшого, ніж просто файл manifest.json та скрипт контенту. Розширення підпорядковуються обмеженням дистрибутивних систем: обмежений обсяг пам’яті, тимчасові фонові процеси, клієнти, які будь-хто може розпакувати, та затримки у перегляді відгуків. Стабільна робота вимагає як чіткої бізнес-моделі, так і чіткого розуміння меж робочого середовища Chrome.

Основна архітектура розширень: Manifest V3

Надійність продукції починається з того, як Chrome ізолює контексти за допомогою Manifest V3 (MV3). Розширення зазвичай складається з трьох шарів:

  1. Вікно випадання та інтерфейс налаштувань: звичайний HTML/CSS/JS, який працює лише під час відкриття вікна інструментів чи сторінки налаштувань, а потім припиняє свою роботу після того, як користувач виходить.
  2. Скрипти контенту: скрипти, які вставляються на сторінки, що відповідають шаблонам маніфесту. Вони можуть читати та змінювати DOM хоста, але працюють у ізольованому середовищі: є спільний DOM, але немає спільних глобальних змінних JavaScript із сторінкою.
  3. Робочий процес фонової служби: координатор без доступу до DOM. Він обробляє події браузера, мережеві операції, локальну кеш-пам’ять та взаємодіє з вашим бекендом.
+-------------------------------------------------------------+
|                        Browser                              |
|                                                             |
|  +---------------------+        +------------------------+  |
|  |    Content Script   |        |   Popup / Options UI   |  |
|  |  (Reads/Mutates DOM)|        | (Short-lived HTML/JS)  |  |
|  +----------+----------+        +-----------+------------+  |
|             |                               |               |
|             |  chrome.runtime.sendMessage() |               |
|             +--------------+----------------+               |
|                            |                                |
|                            v                                |
|             +-------------------------------+               |
|             |    Background Service Worker  |               |
|             |   (Stateless, Event-driven)   |               |
|             +---------------+---------------+               |
+-----------------------------|-------------------------------+
                              | fetch()
                              v
              +-------------------------------+
              |         Your Backend          |
              |   (Auth, Billing, Database)   |
              +-------------------------------+

Ключовою зміною у MV3 є те, що фонові робочі процеси є тимчасовими. Фонові сторінки версії Manifest V2 могли залишатися в пам’яті; MV3 схильний призупиняти неактивні робочі процеси приблизно через тридцять секунд для економії RAM.

Будь-який токен автентифікації чи лічильник, які зберігаються лише у глобальних змінних робочого процесу, зникають, коли Chrome призупиняє цей процес. Дані, які мають залишатися доступними, повинні зберігатися в іншому місці.

Стан та комунікація: практичний підхід

Оскільки неактивні робочі процеси зникають, стабільний стан має зберігатися у chrome.storage.local або IndexedDB.

Коли автоматизація, яка потребує оплати, має виконуватися на сторінці, скрипт контенту не повинен зберігати конфіденційну інформацію про оплату чи результати перевірки ліцензій. Він має надсилати повідомлення фоновому робочому процесу, який перевіряє сеанс на вашому сервері.

1. Оголошення робочого процесу у manifest.json

{
  "manifest_version": 3,
  "name": "DataExtractor Pro",
  "version": "1.0.0",
  "permissions": [
    "storage"
  ],
  "host_permissions": [
    "https://api.yourproduct.com/*"
  ],
  "background": {
    "service_worker": "background.js",
    "type": "module"
  },
  "content_scripts": [
    {
      "matches": ["https://*.github.com/*"],
      "js": ["content.js"]
    }
  ]
}

2. Скрипт контенту (content.js)

Надішліть набраний повідомлення, щоб розбудити або зв’язатися з працівником:

// content.js
async function runPaidTask() {
  try {
    // Send a message to the background service worker
    const response = await chrome.runtime.sendMessage({
      type: 'VERIFY_AND_EXECUTE',
      payload: { targetElement: '#repo-stars-counter-star' }
    });
    if (!response.success) {
      console.warn('Execution stopped:', response.reason);
      return;
    }    console.log('Action performed successfully:', response.data);
  } catch (error) {
    console.error('Failed to communicate with service worker:', error);
  }
}runPaidTask();

3. Робочий процес без стану (background.js)

Цей працівник обробляє повідомлення, читає інформацію сесії з пам’яті, перевіряє дозволи через віддалений API та відповідає:

// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'VERIFY_AND_EXECUTE') {
    // Return true to indicate we will respond asynchronously
    handleTaskExecution(message.payload).then(sendResponse);
    return true;
  }
});
async function handleTaskExecution(payload) {
  // Read state from storage, never from memory variables
  const { sessionToken } = await chrome.storage.local.get(['sessionToken']);  if (!sessionToken) {
    return { success: false, reason: 'NOT_AUTHENTICATED' };
  }  try {
    const apiResponse = await fetch('https://api.yourproduct.com/v1/verify', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${sessionToken}`
      },
      body: JSON.stringify(payload)
    });    const result = await apiResponse.json();    if (!result.isSubscribed) {
      return { success: false, reason: 'PAYMENT_REQUIRED' };
    }    return { success: true, data: result.data };
  } catch (err) {
    return { success: false, reason: 'NETWORK_ERROR' };
  }
}

Повернення значення true з функції chrome.runtime.onMessage.addListener зберігає канал відкритим для асинхронної функції sendResponse. Без цього return true порт закривається раніше, і викликаючий отримує значення undefined.

Як монетизувати клієнта в браузері

Частою помилкою є розміщення логіки оплати чи надання прав лише всередині пакета розширення.

Усе, що надсилається до браузера — JavaScript, WebAssembly, локальні налаштування — може бути перевірене, розпаковане, змінене та перезавантажене в режимі розробника.

Ніколи не покладайтеся на перевірки з боку клієнта для контролю доступу.

Розглядайте цей розширення як ненадійний фронтенд, який взаємодіє з віддаленим бекендом.

Модель монетизації Архітектурна реалізація Практичні компроміси
Freemium SaaS Клієнт вставляє інтерфейс; важкі обчислення виконуються на центральному API Суворе дотримання правил та передбачувані обмеження; вища вартість хостингу
Bring-Your-Own-Key (BYOK) Користувач надає ключ від постачальника; одноразова ліцензія розблоковує місцеві функції Низькі витрати на обчислення; складно захистити перевірку ліцензій від зворотної інженерії
Підписки для команд/B2B Функції співпраці всередині інструментів, таких як Salesforce, Zendesk чи Jira
Висока готовність до оплати; потрібна система SSO та ретельний перегляд дозволів

Облік коштів зазвичай здійснюється через Stripe, Lemon Squeezy або провайдерів, орієнтованих на розширення, таких як ExtensionPay.

Процес оплати не може знаходитися у вигляді примітивного popup-iframe за сучасних правил CSP. Дія „Апгрейд“ має відкривати хостовану сторінку за допомогою chrome.tabs.create({ url: 'https://yourproduct.com/checkout?user_id=xyz' }). Після оплати сервер фіксує обраний план, а відповідний процес оновлює права користувача під час наступної синхронізації.

Приховані інженерні реалії

Перше масштабне оновлення для тисяч встановлень виявило три постійні обмеження.

1. Нестабільність цільового хостингу

Інтеграція з LinkedIn, Jira чи подібними продуктами означає, що ви не контролюєте рівень представлення інтерфейсу. Зміна назви класу з .action-button-v2 на хешований функціональний клас може миттєво зламати селектори та заповнити чергу підтримки.

Більш ефективні способи мінімізації проблем:

  • Уникайте крихких довгих ланцюгів CSS.
  • Віддавайте перевагу атрибутам aria-label, стабільним даним атрибутам чи семантичним маркерам замість класів стилізації.
  • Надсилайте телеметрію про невдалих запитів до DOM, щоб проблеми ставали видимими ще до того, як ситуація вийде з-під контролю.

2. Вузьке місце у перевірці веб-магазину

Критичні виправлення не створюються миттєво. Кожен пакет проходить автоматичну, а іноді й ручну перевірку від Google, яка може тривати години або кілька робочих днів. Погану версію скрипту контенту неможливо врятувати простим розгортанням у продакшені.

Зменште труднощі під час перевірки, застосовуючи:

  • Запит на найменші можливі права доступу. Широкий параметр <all_urls> призводить до повільнішої перевірки безпеки; краще використовувати конкретні хости, наприклад https://github.com/*.
  • Уникайте використання eval() та завантаження скриптів з інтернету. MV3 забороняє виконання коду з віддалених джерел; логіку потрібно розміщувати всередині пакета.
  • 3. Клієнтські пакети можна декомпілювати

    Файли розширень, встановлені на пристрої, знаходяться на диску у каталозі Extensions профілю Chrome. Офускація уповільнює просте читання, але не заважає людям знайти кінцеві точки, отримати закодовані секрети чи змінити локальний блок if (isPaidUser).

    Проектуйте так, ніби клієнт є відкритим кодом. Аутентифікуйте запити API за допомогою JWT чи куки сеансу та щоразу перевіряйте авторизацію на сервері.

    Коли створювати розширення (і коли його уникати)

    Розширення є корисними у таких випадках:

    • Ви покращуєте існуючий процес роботи: Користувачі залишаються в Gmail, GitHub чи Notion, поки ви додаєте нові функції безпосередньо там.
    • Контекст має значення: Продукт потребує прямого доступу до того, що хтось читає або редагує.
    • Придбання має залишатися недорогим: Чіткий опис у магазині дозволяє захопити користувачів з високою мотивацією до пошуку.

    Уникайте використання моделі, коли:

    • Вам потрібен глибокий доступ до ОС: Розширення не можуть вільно використовувати локальну файлову систему чи продовжувати працювати після закриття браузера.
    • Ризики, пов’язані з платформою, є неприйнятними: Правила маніфесту, алгоритми ранжування та політика можуть змусити до раптових змін.

    Магазин є серйозним каналом дистрибуції, а не просто платформою для побічних проектів. Дотримуйтесь меж ізоляції, проектуйте з урахуванням безстанових працівників та зберігайте процедури верифікації на серверах; саме ця комбінація перетворює розширення на стабільний продукт, а не крихкий скрипт. Команди, які з самого початку враховують ці обмеження, витрачають менше часу на боротьбу з чергами перевірок та несподіваними пошкодженнями DOM пізніше.