Учебное пособие, охватывающее концепции работников службы расширения
Обзор
В этом руководстве представлено введение в сервис-воркеры расширений 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.
Продолжайте строить!
Поздравляю с завершением этого урока 🎉. Продолжайте совершенствовать свои навыки, проходя другие уроки для начинающих:
Расширение | Чему вы научитесь |
---|---|
время чтения | Для автоматической вставки элемента на определенный набор страниц. |
Менеджер вкладок | Создать всплывающее окно, управляющее вкладками браузера. |
Режим фокусировки | Для запуска кода на текущей странице после нажатия на действие расширения. |
Продолжайте исследовать
Чтобы продолжить обучение специалистов по распространению знаний, мы рекомендуем вам изучить следующие статьи: