Учебное пособие, охватывающее концепции работников службы расширения
Обзор
В этом руководстве представлено введение в сервис-воркеры расширений Chrome. В рамках этого руководства вы создадите расширение, которое позволит пользователям быстро переходить к справочным страницам API Chrome с помощью омнибокса. Вы узнаете, как:
- Зарегистрируйте своего сервисного работника и импортируйте модули.
- Выполните отладку вашего работника службы расширения.
- Управление состоянием и обработка событий.
- Запускать периодические события.
- Общайтесь с помощью контент-скриптов.
Прежде чем начать
Это руководство предполагает наличие у вас базового опыта веб-разработки. Рекомендуем ознакомиться с курсами «Расширения 101» и «Hello World» для получения вводных знаний о разработке расширений.
Построить расширение
Начните с создания нового каталога с именем quick-api-reference
для хранения файлов расширения или загрузите исходный код из нашего репозитория примеров GitHub .
Шаг 1: Зарегистрируйте сервисного работника
Создайте файл манифеста в корне проекта и добавьте следующий код:
manifest.json:
{ "manifest_version": 3, "name": "Open extension API reference", "version": "1.0.0", "icons": { "16": "images/icon-16.png", "128": "images/icon-128.png" }, "background": { "service_worker": "service-worker.js" } }
Расширения регистрируют свой сервис-воркер в манифесте, который занимает всего один JavaScript-файл. Нет необходимости вызывать navigator.serviceWorker.register()
, как это делается на веб-странице.
Создайте папку images
, а затем загрузите в нее иконки .
Ознакомьтесь с первыми шагами руководства по чтению, чтобы узнать больше о метаданных расширения и значках в манифесте.
Шаг 2: Импорт нескольких модулей Service Worker
Наш сервис-воркер реализует две функции. Для удобства поддержки мы реализуем каждую функцию в отдельном модуле. Во-первых, нам нужно объявить сервис-воркер как модуль ES в нашем манифесте, что позволит нам импортировать модули в наш сервис-воркер:
manifest.json:
{ "background": { "service_worker": "service-worker.js", "type": "module" }, }
Создайте файл service-worker.js
и импортируйте два модуля:
import './sw-omnibox.js'; import './sw-tips.js';
Создайте эти файлы и добавьте в каждый из них журнал консоли.
sw-omnibox.js:
console.log("sw-omnibox.js");
sw-tips.js:
console.log("sw-tips.js");
См. раздел Импорт скриптов , чтобы узнать о других способах импорта нескольких файлов в Service Worker.
Необязательно: отладка сервисного работника
Я объясню, как найти логи сервис-воркера и определить момент его завершения. Сначала следуйте инструкциям по загрузке распакованного расширения .
Через 30 секунд вы увидите сообщение «Service Worker (неактивен)», что означает, что сервис-воркер завершил работу. Нажмите на ссылку «Service Worker (неактивен)», чтобы просмотреть её. Это показано на следующей анимации.
Вы заметили, что проверка сервис-воркера активировала его? Открытие сервис-воркера в DevTools сохранит его активным. Чтобы убедиться, что ваше расширение работает корректно после завершения работы сервис-воркера, не забудьте закрыть DevTools.
Теперь сломайте расширение, чтобы узнать, где искать ошибки. Один из способов сделать это — удалить «.js» из импорта './sw-omnibox.js'
в файле service-worker.js
. Chrome не сможет зарегистрировать сервис-воркер.
Вернитесь на страницу chrome://extensions и обновите расширение. Вы увидите две ошибки:
Service worker registration failed. Status code: 3. An unknown error occurred when fetching the script.
Дополнительные способы отладки работника службы расширений см. в разделе Отладка расширений .
Шаг 4: Инициализация состояния
Chrome отключит сервис-воркеры, если они не нужны. Мы используем API chrome.storage
для сохранения состояния между сеансами сервис-воркеров. Для доступа к хранилищу необходимо запросить разрешение в манифесте:
manifest.json:
{ ... "permissions": ["storage"], }
Сначала сохраните предложения по умолчанию в хранилище. Мы можем инициализировать состояние при первой установке расширения, прослушивая событие runtime.onInstalled()
:
sw-omnibox.js:
... // Save default API suggestions chrome.runtime.onInstalled.addListener(({ reason }) => { if (reason === 'install') { chrome.storage.local.set({ apiSuggestions: ['tabs', 'storage', 'scripting'] }); } });
Сервис-воркеры не имеют прямого доступа к объекту окна и, следовательно, не могут использовать window.localStorage
для хранения значений. Кроме того, сервис-воркеры — это кратковременные среды выполнения; они многократно завершаются в течение сеанса браузера пользователя, что делает их несовместимыми с глобальными переменными. Вместо этого используйте chrome.storage.local
, который хранит данные на локальном компьютере.
Ознакомьтесь с разделом Сохранение данных вместо использования глобальных переменных, чтобы узнать о других вариантах хранения для работников служб расширения.
Шаг 5: Зарегистрируйте свои мероприятия
Все обработчики событий должны быть статически зарегистрированы в глобальной области видимости сервис-воркера. Другими словами, обработчики событий не должны быть вложены в асинхронные функции. Таким образом, Chrome может гарантировать восстановление всех обработчиков событий в случае перезагрузки сервис-воркера.
В этом примере мы будем использовать API chrome.omnibox
, но сначала нам необходимо объявить триггер ключевого слова omnibox в манифесте:
manifest.json:
{ ... "minimum_chrome_version": "102", "omnibox": { "keyword": "api" }, }
Теперь зарегистрируйте прослушиватели событий омнибокса на верхнем уровне скрипта. Когда пользователь вводит ключевое слово омнибокса ( api
) в адресной строке, а затем нажимает клавишу Tab или пробел, Chrome отображает список подсказок на основе ключевых слов из хранилища. Событие onInputChanged()
, которое принимает текущий пользовательский ввод и объект suggestResult
, отвечает за заполнение этих подсказок.
sw-omnibox.js:
... const URL_CHROME_EXTENSIONS_DOC = 'https://developer.chrome.com/docs/extensions/reference/'; const NUMBER_OF_PREVIOUS_SEARCHES = 4; // Display the suggestions after user starts typing chrome.omnibox.onInputChanged.addListener(async (input, suggest) => { await chrome.omnibox.setDefaultSuggestion({ description: 'Enter a Chrome API or choose from past searches' }); const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions'); const suggestions = apiSuggestions.map((api) => { return { content: api, description: `Open chrome.${api} API` }; }); suggest(suggestions); });
После того как пользователь выберет предложение, onInputEntered()
откроет соответствующую страницу справки Chrome API.
sw-omnibox.js:
... // Open the reference page of the chosen API chrome.omnibox.onInputEntered.addListener((input) => { chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input }); // Save the latest keyword updateHistory(input); });
Функция updateHistory()
принимает входные данные омнибокса и сохраняет их в storage.local
. Таким образом, последний поисковый запрос можно будет использовать позже в качестве подсказки для омнибокса.
sw-omnibox.js:
... async function updateHistory(input) { const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions'); apiSuggestions.unshift(input); apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES); return chrome.storage.local.set({ apiSuggestions }); }
Шаг 6: Настройте повторяющееся событие
Методы setTimeout()
или setInterval()
обычно используются для выполнения отложенных или периодических задач. Однако эти API могут давать сбои, поскольку планировщик отменяет таймеры при завершении работы сервис-воркера. Вместо этого расширения могут использовать API chrome.alarms
.
Начните с запроса разрешения "alarms"
в манифесте:
manifest.json:
{ ... "permissions": ["storage"], "permissions": ["storage", "alarms"], }
Расширение соберёт все советы, выберет один случайным образом и сохранит его в хранилище. Мы создадим будильник, который будет срабатывать раз в день, чтобы обновить совет. Будильник не сохраняется при закрытии Chrome. Поэтому нам нужно проверить наличие будильника и создать его, если его нет.
sw-tips.js:
// Fetch tip & save in storage const updateTip = async () => { const response = await fetch('https://chrome.dev/f/extension_tips/'); const tips = await response.json(); const randomIndex = Math.floor(Math.random() * tips.length); return chrome.storage.local.set({ tip: tips[randomIndex] }); }; const ALARM_NAME = 'tip'; // Check if alarm exists to avoid resetting the timer. // The alarm might be removed when the browser session restarts. async function createAlarm() { const alarm = await chrome.alarms.get(ALARM_NAME); if (typeof alarm === 'undefined') { chrome.alarms.create(ALARM_NAME, { delayInMinutes: 1, periodInMinutes: 1440 }); updateTip(); } } createAlarm(); // Update tip once a day chrome.alarms.onAlarm.addListener(updateTip);
Шаг 7: Общайтесь с другими контекстами
Расширения используют скрипты контента для чтения и изменения содержимого страницы. Когда пользователь посещает страницу со справочной информацией по API Chrome, скрипт контента расширения обновляет страницу, добавляя на неё совет дня. Он отправляет сообщение с запросом совета дня у сервис-воркера.
Начните с объявления скрипта содержимого в манифесте и добавьте шаблон соответствия, соответствующий справочной документации Chrome API .
manifest.json:
{ ... "content_scripts": [ { "matches": ["https://developer.chrome.com/docs/extensions/reference/*"], "js": ["content.js"] } ] }
Создайте новый файл контента. Следующий код отправляет сообщение сервисному работнику с запросом подсказки. Затем добавляет кнопку, которая открывает всплывающее окно с подсказкой расширения. Этот код использует новый API веб-платформы Popover .
content.js:
(async () => { // Sends a message to the service worker and receives a tip in response const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' }); const nav = document.querySelector('.upper-tabs > nav'); const tipWidget = createDomElement(` <button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;"> <span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span> </button> `); const popover = createDomElement( `<div id='tip-popover' popover style="margin: auto;">${tip}</div>` ); document.body.append(popover); nav.append(tipWidget); })(); function createDomElement(html) { const dom = new DOMParser().parseFromString(html, 'text/html'); return dom.body.firstElementChild; }
Последний шаг — добавление обработчика сообщений в наш сервис-воркер, который отправляет ответ на контент-скрипт с ежедневными чаевыми.
sw-tips.js:
... // Send tip to content script via messaging chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.greeting === 'tip') { chrome.storage.local.get('tip').then(sendResponse); return true; } });
Проверьте, работает ли это
Убедитесь, что структура файла вашего проекта выглядит следующим образом:
Загрузите свое расширение локально
Чтобы загрузить распакованное расширение в режиме разработчика, следуйте инструкциям в разделе Hello world .
Открыть страницу со справочной информацией
- Введите ключевое слово «api» в адресную строку браузера.
- Нажмите «tab» или «пробел».
- Введите полное имя API.
- ИЛИ выберите из списка прошлых поисков
- Откроется новая страница со справочной информацией по API Chrome.
Это должно выглядеть так:

Откройте кончик дня
Нажмите кнопку «Подсказка», расположенную на панели навигации, чтобы открыть подсказку по расширению.

🎯 Потенциальные улучшения
Исходя из того, что вы узнали сегодня, попробуйте выполнить любое из следующих действий:
- Изучите другой способ реализации предложений омнибокса.
- Создайте собственное модальное окно для отображения подсказки по расширению.
- Откройте дополнительную страницу к справочным страницам API веб-расширений MDN.
Продолжайте строить!
Поздравляю с завершением этого урока 🎉. Продолжайте совершенствовать свои навыки, проходя другие уроки для начинающих:
Расширение | Чему вы научитесь |
---|---|
время чтения | Для автоматической вставки элемента на определенный набор страниц. |
Менеджер вкладок | Создать всплывающее окно, управляющее вкладками браузера. |
Режим фокусировки | Для запуска кода на текущей странице после нажатия на действие расширения. |
Продолжайте исследовать
Чтобы продолжить обучение специалистов по распространению знаний, мы рекомендуем вам изучить следующие статьи: