Obsługa zdarzeń za pomocą skryptów service worker

Samouczek omawiający koncepcje skryptu usługi rozszerzenia

Omówienie

Ten samouczek zawiera wprowadzenie do procesów roboczych rozszerzenia Chrome. W ramach utworzysz rozszerzenie, które pozwoli użytkownikom szybko przejść do dokumentacji interfejsu API Chrome za pomocą omniboksu. Zapoznasz się z tymi zagadnieniami:

  • Zarejestruj skrypt service worker i moduły importu.
  • Debuguj skrypt usługi rozszerzenia.
  • zarządzać stanem i obsługiwać zdarzenia;
  • Wywołuj zdarzenia okresowe.
  • Komunikacja za pomocą skryptów treści.

Zanim rozpoczniesz

W tym przewodniku zakładamy, że masz już podstawowe doświadczenie w tworzeniu stron internetowych. Zalecamy sprawdzenie Rozszerzenia 101 i Hello World zawierające wprowadzenie do w tworzeniu rozszerzeń.

Tworzenie rozszerzenia

Zacznij od utworzenia nowego katalogu o nazwie quick-api-reference, w którym będą przechowywane pliki rozszerzeń. pobierz kod źródłowy z naszego przykładowego repozytorium na GitHubie.

Krok 1. Zarejestruj skrypt service worker

Utwórz plik manifest w katalogu głównym projektu i dodaj ten kod:

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"
  }
}

Rozszerzenia rejestrują swój skrypt service worker w pliku manifestu, który wymaga tylko jednego pliku JavaScript. Nie musisz dzwonić pod numer navigator.serviceWorker.register(), jak robisz to na stronie internetowej.

Utwórz folder images i pobierz do niego ikony.

Aby dowiedzieć się więcej o metadanych i ikonach rozszerzenia w pliku manifestu, zapoznaj się z pierwszymi krokami samouczka dotyczącego czasu czytania.

Krok 2. Zaimportuj wiele modułów skryptu service worker

Skrypt service worker implementuje 2 funkcje. Aby ułatwić ich obsługę, wdrożymy każdą z nich w osobnym module. Najpierw musimy zadeklarować skrypt service worker jako moduł ES w pliku manifestu, co umożliwia importowanie modułów do niego:

manifest.json:

{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

Utwórz plik service-worker.js i zaimportuj 2 moduły:

import './sw-omnibox.js';
import './sw-tips.js';

Utwórz te pliki i dodaj do każdego z nich dziennik konsoli.

sw-omniboks.js:

console.log("sw-omnibox.js");

sw-tips.js:

console.log("sw-tips.js");

Zapoznaj się z artykułem Importowanie skryptów, aby poznać inne sposoby importowania wielu plików za pomocą skryptu service worker.

Opcjonalnie: debugowanie skryptu service worker

Wyjaśnię, jak znaleźć logi skryptu service worker i dowiedzieć się, kiedy został wyłączony. Najpierw wykonaj czynności opisane w artykule Wczytywanie rozszerzenia bez pakietu.

Po 30 sekundach pojawi się komunikat „Skrypt service worker (nieaktywny)”. co oznacza, że skrypt service worker został zakończony. Kliknij skrypt service worker (nieaktywny) aby go sprawdzić. Widać to na animacji poniżej.

Czy zauważyłeś, że pracownik usługi wybudził urządzenie? Otwarcie skryptu service worker w narzędziach deweloperskich pozostanie aktywny. Aby upewnić się, że rozszerzenie będzie działać prawidłowo po zakończeniu działania skryptu service worker, zamknij Narzędzia deweloperskie.

Teraz podziel rozszerzenie, aby dowiedzieć się, gdzie znaleźć błędy. Można to zrobić na przykład przez usunięcie pliku „.js” z importu './sw-omnibox.js' w pliku service-worker.js. Chrome nie będzie w stanie zarejestrować skryptu service worker.

Wróć na stronę chrome://extensions i odśwież rozszerzenie. Pojawią się 2 błędy:

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

Więcej sposobów debugowania skryptu usługi rozszerzenia znajdziesz w artykule Debugowanie rozszerzeń.

Krok 4. Zainicjuj stan

Chrome wyłączy instancje service worker, jeśli nie będą potrzebne. Używamy interfejsu API chrome.storage, aby zachowywać stan w różnych sesjach skryptu service worker. Aby uzyskać dostęp do pamięci, musisz poprosić o odpowiednie uprawnienia w pliku manifestu:

manifest.json:

{
  ...
  "permissions": ["storage"],
}

Najpierw zapisz domyślne sugestie w pamięci. Możemy zainicjować stan podczas pierwszej instalacji rozszerzenia, nasłuchując zdarzenia runtime.onInstalled():

sw-omniboks.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

Skrypty service worker nie mają bezpośredniego dostępu do obiektu window i nie mogą używać window.localStorage do przechowywania wartości. Skrypty service worker to także środowiska wykonawcze o krótkim czasie działania. są wielokrotnie zamykane przez całą sesję przeglądarki użytkownika, co sprawia, że są niezgodne z zmiennych globalnych. Zamiast tego użyj chrome.storage.local, który przechowuje dane na komputerze lokalnym.

Aby poznać inne opcje pamięci masowej dla mechanizmów Service Worker, zapoznaj się z sekcją Utrwalaj dane zamiast używać zmiennych globalnych.

Krok 5. Zarejestruj zdarzenia

Wszystkie detektory zdarzeń muszą być statycznie zarejestrowane w globalnym zakresie skryptu service worker. Inaczej mówiąc, detektory zdarzeń nie powinny być zagnieżdżone w funkcjach asynchronicznych. Dzięki temu w przypadku ponownego uruchomienia skryptu service worker Chrome może mieć pewność, że wszystkie moduły obsługi zdarzeń zostaną przywrócone.

W tym przykładzie użyjemy interfejsu API chrome.omnibox, ale najpierw musimy zadeklarować regułę słowa kluczowego omniboksu w pliku manifestu:

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}

Teraz zarejestruj detektory zdarzeń omniboksu na najwyższym poziomie skryptu. Gdy użytkownik wpisze na pasku adresu słowo kluczowe z omniboksu (api), a następnie klawisz Tab lub spację, Chrome wyświetli listę sugestii na podstawie zapisanych słów kluczowych. Za wypełnianie tych sugestii odpowiada zdarzenie onInputChanged(), które pobiera dane wejściowe bieżącego użytkownika oraz obiekt suggestResult.

sw-omniboks.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);
});

Gdy użytkownik wybierze sugestię, onInputEntered() otworzy odpowiednią stronę z informacjami o interfejsie API Chrome.

sw-omniboks.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);
});

Funkcja updateHistory() pobierze dane wejściowe omniboksu i zapisze je w storage.local. Dzięki temu ostatnie wyszukiwane hasło może zostać później użyte jako sugestia omniboksu.

sw-omniboks.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 });
}

Krok 6. Skonfiguruj wydarzenie cykliczne

Metody setTimeout() lub setInterval() są zwykle używane do wykonywania działań opóźnionych lub okresowych zadania. Te interfejsy API mogą jednak przestać działać, ponieważ algorytm szeregowania anuluje liczniki czasu, gdy usługa instancja robocza została zakończona. Zamiast tego mogą używać interfejsu API chrome.alarms.

Zacznij od wysłania prośby o przyznanie uprawnień "alarms" w pliku manifestu. Aby pobrać wskazówki dotyczące rozszerzeń z lokalizacji hostowanej zdalnie, musisz poprosić o uprawnienia hosta:

manifest.json:

{
  ...
  "permissions": ["storage"],
  "permissions": ["storage", "alarms"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

Rozszerzenie pobierze wszystkie wskazówki, wybierze jedną z nich losowo i zapisze ją w pamięci. Utworzymy alarm, który będzie uruchamiany raz dziennie w celu zaktualizowania wskazówki. Alarmy nie są zapisywane po zamknięciu Chrome. Musimy więc sprawdzić, czy alarm istnieje, i utworzyć go, jeśli go nie ma.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  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);

Krok 7. Komunikuj się z innymi kontekstami

Rozszerzenia korzystają ze skryptów treści do odczytywania i modyfikowania zawartości strony. Gdy użytkownik odwiedza stronę z informacjami o interfejsie API Chrome, skrypt treści rozszerzenia aktualizuje tę stronę o wskazówkę dnia. Wysyła wiadomość z prośbą o wskazówkę dnia do pracownika obsługi klienta.

Zacznij od zadeklarowania skryptu treści w pliku manifestu i dodaj wzorzec dopasowania odpowiadający dokumentacji referencyjnej interfejsu API Chrome.

manifest.json:

{
  ...
  "content_scripts": [
    {
      "matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
      "js": ["content.js"]
    }
  ]
}

Utwórz nowy plik treści. Poniższy kod wysyła do skryptu service worker z prośbą o wskazówkę. Następnie dodaje przycisk powodujący otwarcie wyskakującego okienka ze wskazówką dotyczącą rozszerzenia. Ten kod wykorzystuje nową platformę internetową Popover API.

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;
}

Ostatnim krokiem jest dodanie do naszego mechanizmu obsługi wiadomości modułu obsługi wiadomości, który wysyła odpowiedź do skryptu treści z codzienną wskazówką.

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;
  }
});

Sprawdź, czy wszystko działa

Sprawdź, czy struktura plików w projekcie wygląda tak:

Zawartość folderu rozszerzenia: images folder, manifest.json, service-worker.js, sw-omnibox.js, sw-tips.js
oraz content.js

Wczytywanie rozszerzenia lokalnie

Aby wczytać rozszerzenie bez pakietu w trybie programisty, wykonaj czynności opisane w artykule Hello world.

Otwieranie strony referencyjnej

  1. Wpisz słowo kluczowe „api” na pasku adresu przeglądarki.
  2. Naciśnij Tab. lub „spacja”.
  3. Wpisz pełną nazwę interfejsu API.
    • LUB wybrać z listy wcześniejszych wyszukiwań.
  4. Otworzy się nowa strona z informacjami o interfejsie API Chrome.

Powinien on wyglądać podobnie do tego:

Krótkie omówienie interfejsu API z otwieraniem dokumentacji interfejsu API środowiska wykonawczego
Rozszerzenie szybkiego interfejsu API otwierające środowisko wykonawcze.

Otwórz wskazówkę dnia

Kliknij przycisk Wskazówka znajdujący się na pasku nawigacyjnym, aby otworzyć wskazówkę dotyczącą rozszerzenia.

Otwórz dzienną wskazówkę w:
Szybkie rozszerzenie interfejsu API, które otwiera wskazówkę dnia.
.
.

🎯 Potencjalne ulepszenia

Na podstawie tego, czego się dziś nauczysz, spróbuj wykonać jedną z tych czynności:

  • Poznaj inny sposób implementacji sugestii omniboksu.
  • Utwórz własny, niestandardowy modalny panel do wyświetlania wskazówek dotyczących rozszerzenia.
  • Otwórz dodatkową stronę z informacjami o interfejsie API rozszerzeń sieciowych w MDN.

Rozwijaj dalej!

Gratulujemy ukończenia samouczka 🎉. Zdobywaj kolejne umiejętności i zdobywaj kolejne samouczki dla początkujących:

Rozszerzenie Czego się nauczysz
Czas czytania Aby automatycznie wstawić element w określonym zestawie stron.
Menedżer kart Aby utworzyć wyskakujące okienko do zarządzania kartami przeglądarki.
Tryb pełnej koncentracji Aby uruchomić kod na bieżącej stronie po kliknięciu działania rozszerzenia.

Przeglądaj dalej

Jeśli chcesz kontynuować ścieżkę szkoleniową dotyczącą skryptu service worker rozszerzenia, zapoznaj się z tymi artykułami: