Manifest V3 ist jetzt die einzige Option für neue Chrome-Erweiterungen und verändert die Architektur erheblich: Hintergrundseiten wurden zu kurzlebigen Servicemitarbeitern, Remote-Codeausführung ist verboten und das Abfangen von Netzwerken wurde auf eine deklarative API verlagert. Wenn Sie die Erweiterungsentwicklung auf V2 gelernt haben, funktionieren einige Gewohnheiten nicht mehr. Dieser Leitfaden erstellt eine vollständige, funktionierende Erweiterung von Grund auf nach den V3-Regeln.
📋 Table of Contents
- Was wir bauen
- Schritt 1: Projektstruktur und das Manifest
- Schritt 2: Der Servicemitarbeiter ist keine Hintergrundseite
- Schritt 3: Das Inhaltsskript
- Schritt 4: Das Popup
- Schritt 5: Speicheroptionen
- Schritt 6: Netzwerküberwachung geändert
- Schritt 7: Laden und Debuggen
- Schritt 8: Veröffentlichung
- Häufige Fehler
- Fazit
Was wir bauen
Eine Seitenanmerkungserweiterung: Sie fügt ein Symbolleisten-Popup hinzu, fügt ein Inhaltsskript ein, das ausgewählten Text auf jeder Seite hervorhebt, speichert Hervorhebungen pro URL und synchronisiert sie überchrome.storage. Es übt jeden Teil von V3 aus, den Sie tatsächlich verwenden werden – Popup-Benutzeroberfläche, Inhaltsskript, Servicemitarbeiter, Nachrichten, Speicher und Berechtigungen.
Schritt 1: Projektstruktur und das Manifest
Erstellen Sie ein Verzeichnis mit diesen Dateien. Das Manifest ist der Einstiegspunkt und Chrome liest es zuerst.
my-extension/
manifest.json
background.js
content.js
popup.html
popup.js
styles.css
icons/icon16.png icon48.png icon128.png
Das Manifest deklariert Version 3, Ihre Einstiegspunkte und Berechtigungen. Beschränken Sie die Berechtigungen auf ein Minimum – jede zusätzliche Berechtigung verlangsamt die Überprüfung und schreckt Benutzer bei der Installation ab.
{
"manifest_version": 3,
"name": "Page Highlighter",
"version": "1.0.0",
"description": "Highlight and save text on any page.",
"permissions": ["storage", "activeTab", "scripting"],
"host_permissions": ["http://*/*", "https://*/*"],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"css": ["styles.css"],
"run_at": "document_idle"
}
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
Bevorzugen SieactiveTab über weitreichende Host-Berechtigungen, wenn Sie können. Der Zugriff auf die aktuelle Registerkarte wird erst gewährt, nachdem der Benutzer auf Ihr Erweiterungssymbol geklickt hat, was bei der Überprüfung viel einfacher zu rechtfertigen ist.
Schritt 2: Der Servicemitarbeiter ist keine Hintergrundseite
Dies ist die größte V3-Änderung. Ihr Hintergrundskript ist ein Service-Worker, der Chrome im Leerlauf beendet und beim nächsten Ereignis neu startet. Alle Variablen, die Sie auf der obersten Ebene festgelegt haben, sind nach der Beendigung verschwunden. Staat muss inchrome.storageleben , nicht im Gedächtnis.
// background.js
// WRONG under V3 — this resets every time the worker restarts.
// let highlightCount = 0;
chrome.runtime.onInstalled.addListener(async () => {
const { highlights } = await chrome.storage.local.get('highlights');
if (!highlights) {
await chrome.storage.local.set({ highlights: {} });
}
});
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'SAVE_HIGHLIGHT') {
saveHighlight(message.payload).then(() => sendResponse({ ok: true }));
// Returning true keeps the message channel open for the async reply.
return true;
}
});
async function saveHighlight({ url, text }) {
const { highlights = {} } = await chrome.storage.local.get('highlights');
const forPage = highlights[url] || [];
forPage.push({ text, createdAt: Date.now() });
highlights[url] = forPage;
await chrome.storage.local.set({ highlights });
}
Dasreturn true im Nachrichten-Listener ist leicht zu übersehen und verursacht einen klassischen Fehler: Ihr asynchroner Handler wird abgeschlossen, aber der Absender erhält nie die Antwort, da Chrome den Kanal geschlossen hat, als der Listener undefiniert zurückgegeben hat.
Schritt 3: Das Inhaltsskript
Inhaltsskripte werden im DOM der Seite ausgeführt, jedoch in einer isolierten JavaScript-Welt. Sie können das DOM lesen und ändern, aber die eigenen JavaScript-Variablen der Seite nicht sehen. Bei dieser Isolierung handelt es sich um eine Sicherheitsfunktion und nicht um eine zu umgehende Einschränkung.
// content.js
document.addEventListener('mouseup', async () => {
const selection = window.getSelection();
const text = selection.toString().trim();
if (text.length < 3) return;
const range = selection.getRangeAt(0);
const mark = document.createElement('mark');
mark.className = 'ext-highlight';
try {
range.surroundContents(mark);
} catch {
// surroundContents throws when the selection crosses element boundaries.
return;
}
await chrome.runtime.sendMessage({
type: 'SAVE_HIGHLIGHT',
payload: { url: location.href, text }
});
selection.removeAllRanges();
});
Beachten Sie dastry/catch umsurroundContents. Es wird immer dann ausgelöst, wenn die Auswahl mehrere Elemente umfasst, was auf echten Seiten ständig vorkommt. Die Handhabung macht den Unterschied zwischen einer Erweiterung, die auf Ihrer Testseite funktioniert, und einer Erweiterung, die überall funktioniert.
Schritt 4: Das Popup
Das Popup ist eine gewöhnliche Webseite mit Zugriff auf Erweiterungs-APIs. Es wird jedes Mal zerstört, wenn es geschlossen wird. Behandeln Sie es daher als zustandslos und lesen Sie es beim Öffnen aus dem Speicher.
<!-- popup.html -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"></head>
<body style="width:320px;font:14px system-ui;padding:12px">
<h1 style="font-size:15px;margin:0 0 8px">Highlights on this page</h1>
<ul id="list"></ul>
<script src="popup.js"></script>
</body>
</html>
Inline-Skripte werden durch die Inhaltssicherheitsrichtlinie der Erweiterung blockiert, daher<script src> Der Verweis ist obligatorisch – Sie können JavaScript nicht direkt in den HTML-Code einfügen.
// popup.js
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const { highlights = {} } = await chrome.storage.local.get('highlights');
const items = highlights[tab.url] || [];
const list = document.getElementById('list');
if (items.length === 0) {
list.innerHTML = '<li>No highlights yet.</li>';
} else {
for (const item of items) {
const li = document.createElement('li');
li.textContent = item.text; // textContent, never innerHTML
list.appendChild(li);
}
}
Verwenden SietextContent stattinnerHTML für jeden Wert, der von einer Webseite stammt. Bei hervorgehobenem Text handelt es sich um vom Angreifer kontrollierte Daten, und die Erstellung von HTML daraus ist eine einfache XSS-Route in den privilegierten Kontext Ihrer Erweiterung.
Schritt 5: Speicheroptionen
Chrome bietet Ihnen drei Speicherbereiche und die falsche Auswahl führt zu stillen Fehlern.
| Fläche | Kontingent | Verwenden Sie für |
|---|---|---|
storage.local |
~10 MB (unbegrenzt mit Genehmigung) | Massendaten, zwischengespeicherter Inhalt |
storage.sync |
~100 KB insgesamt, 8 KB pro Element | Kleine Benutzereinstellungen, geräteübergreifend synchronisiert |
storage.session |
~10 MB, nur Speicher | Daten, die einen Browser-Neustart nicht überleben sollten |
Der häufigste Fehler besteht darin, Benutzerinhalte instorage.syncabzulegen weil die Synchronisierung wünschenswert klingt. Das Limit von 8 KB pro Element wird schnell erreicht und Schreibvorgänge schlagen dann fehl – oft unbemerkt, wenn Sie nicht auf Fehler prüfen.
Schritt 6: Netzwerküberwachung geändert
Die BlockierungwebRequest API ist weg. Wenn Sie Anfragen blockieren oder umleiten müssen, verwenden SiedeclarativeNetRequest, wo Sie statische Regeln registrieren, die Chrome selbst auswertet. Ihre Nebenstelle sieht die Anfrage nie.
{
"permissions": ["declarativeNetRequest"],
"declarative_net_request": {
"rule_resources": [{
"id": "ruleset_1",
"enabled": true,
"path": "rules.json"
}]
}
}
Dies ist von Natur aus restriktiver und deshalb mussten mehrere Werbeblocker neu geschrieben werden. Wenn der Kernwert Ihrer Erweiterung von der Überprüfung der Anforderungstexte zur Laufzeit abhängt, wird sie von V3 möglicherweise nicht wirklich unterstützt.
Schritt 7: Laden und Debuggen
Öffnen Siechrome://extensions, aktivieren Sie den Entwicklermodus und wählen Sie „Ungepackt laden“. Es gibt drei separate Konsolen und wenn man weiß, welche man öffnen muss, spart man Stunden:
- Servicemitarbeiter: Klicken Sie auf Ihrer Erweiterungskarte auf den Link „Servicemitarbeiter“
- Popup: Klicken Sie mit der rechten Maustaste auf das Popup und wählen Sie „Inspizieren “. Inhaltsskript:
- die normale Seiten-DevTools-Konsole, wobei der Kontextselektor auf Ihre Erweiterungeingestellt ist Wenn der Servicemitarbeiter tot zu sein scheint, ist das zu erwarten – er wird nach etwa 30 Sekunden Inaktivität beendet. Lösen Sie ein Ereignis aus und es wird neu gestartet.
Schritt 8: Veröffentlichung
Schritt 8: Veröffentlichung
Komprimieren Sie den Inhalt des Erweiterungsverzeichnisses (nicht den beiliegenden Ordner) und laden Sie ihn über das Chrome Web Store Developer Dashboard hoch, wofür eine einmalige Registrierungsgebühr anfällt. Die Überprüfungszeit hängt stark von Ihren Berechtigungen ab: Eine Erweiterung, die nuractiveTabverwendet undstorage löscht die Überprüfung normalerweise schnell, während umfassende Host-Berechtigungen plusscripting erfordern eine manuelle Überprüfung und können erheblich länger dauern. Schreiben Sie eine klare Begründung für jede Erlaubnis in der Auflistung – Prüfer lehnen vage Erklärungen ab.
Häufige Fehler
Status in Service-Worker-Globals speichern. Beim Beenden verschwindet es. Verwenden Siechrome.storage.
Vergessenreturn true in asynchronen Nachrichten-Listenern. Die Antwort kommt nie und der Fehler bleibt stumm.
Anfrage<all_urls> wennactiveTab würde tun. Es verlangsamt die Überprüfung und reduziert die Anzahl der Installationen.
Remote-Code wird geladen. V3 verbietet es komplett. Der gesamte ausführbare Code muss im Paket enthalten sein, d. h. keine CDN-Skripte und keineval.
Fazit
Die Entwicklung von Manifest V3 lässt sich auf einige Disziplinen reduzieren:Behandeln Sie den Servicemitarbeiter als zustandslos und behalten Sie alle Zustände inchrome.storagebei , fordern Sie die engsten Berechtigungen an, die funktionieren, verwenden SietextContent Denken Sie bei allem, was von einer Seite stammt, anreturn true für asynchrone Nachrichtenhandler und liefern Sie jede Codezeile im Paket. Bauen Sie von Anfang an unter Berücksichtigung dieser Einschränkungen und die Plattform steht Ihnen nicht im Weg – die Nachrüstung auf ein V2-förmiges Design ist der größte Nachteil.
🔗 Share this article
✍️ Leave a Comment