Unterstützung von CSS in JS in den Entwicklertools

Alex Rudenko
Alex Rudenko

In diesem Artikel geht es um die Unterstützung von CSS-in-JS in den DevTools, die seit Chrome 85 verfügbar ist. Außerdem erfahren Sie, was wir unter CSS-in-JS verstehen und wie es sich von regulärem CSS unterscheidet, das schon lange von den DevTools unterstützt wird.

Was ist CSS-in-JS?

Die Definition von CSS-in-JS ist ziemlich vage. Im weitesten Sinne ist es ein Ansatz zum Verwalten von CSS-Code mit JavaScript. Das kann beispielsweise bedeuten, dass der CSS-Inhalt mit JavaScript definiert und die endgültige CSS-Ausgabe von der App in Echtzeit generiert wird.

Im Kontext von DevTools bedeutet CSS-in-JS, dass der CSS-Inhalt mithilfe von CSSOM APIs in die Seite eingefügt wird. Normales CSS wird mithilfe von <style>- oder <link>-Elementen eingefügt und hat eine statische Quelle (z.B. einen DOM-Knoten oder eine Netzwerkressource). CSS-in-JS hat dagegen oft keine statische Quelle. Ein Sonderfall ist hier, dass der Inhalt eines <style>-Elements mit der CSSOM API aktualisiert werden kann, wodurch die Quelle nicht mehr mit dem tatsächlichen CSS-Stylesheet synchronisiert ist.

Wenn Sie eine CSS-in-JS-Bibliothek verwenden (z.B. styled-component, Emotion oder JSS), kann die Bibliothek je nach Entwicklungsmodus und Browser Stile mithilfe von CSSOM APIs einschleusen.

Sehen wir uns einige Beispiele an, wie Sie ein Stylesheet mit der CSSOM API einfügen können, ähnlich wie es bei CSS-in-JS-Bibliotheken der Fall ist.

// Insert new rule to an existing CSS stylesheet
const element = document.querySelector('style');
const stylesheet = element.sheet;
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

Sie können auch ein ganz neues Stylesheet erstellen:

// Create a completely new stylesheet
const stylesheet = new CSSStyleSheet();
stylesheet.replaceSync('.some { color: blue; }');
stylesheet.insertRule('.some { color: green; }');

// Apply constructed stylesheet to the document
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];

CSS-Unterstützung in DevTools

In den DevTools ist der Bereich Styles die am häufigsten verwendete Funktion im Zusammenhang mit CSS. Im Bereich Stile sehen Sie, welche Regeln auf ein bestimmtes Element angewendet werden. Sie können die Regeln bearbeiten und die Änderungen auf der Seite in Echtzeit sehen.

Vor dem letzten Jahr war die Unterstützung für CSS-Regeln, die mit CSSOM APIs geändert wurden, eher begrenzt: Sie konnten die angewendeten Regeln nur sehen, aber nicht bearbeiten. Unser Hauptziel im letzten Jahr war es, die Bearbeitung von CSS-in-JS-Regeln über den Bereich „Styles“ zu ermöglichen. Manchmal werden CSS-in-JS-Stile auch als „erstellt“ bezeichnet, um anzugeben, dass sie mit Web-APIs erstellt wurden.

Sehen wir uns an, wie die Bearbeitung von Stilen in den DevTools funktioniert.

Stilbearbeitungsmechanismus in den Entwicklertools

Stilbearbeitungsmechanismus in den Entwicklertools

Wenn Sie in den DevTools ein Element auswählen, wird der Bereich Styles angezeigt. Im Bereich Styles wird der CDP-Befehl CSS.getMatchedStylesForNode ausgegeben, um CSS-Regeln abzurufen, die für das Element gelten. CDP steht für Chrome DevTools Protocol und ist eine API, mit der die DevTools-Frontend-Anwendung zusätzliche Informationen zur geprüften Seite abrufen kann.

Wenn CSS.getMatchedStylesForNode aufgerufen wird, werden alle Stylesheets im Dokument identifiziert und mit dem CSS-Parser des Browsers geparst. Anschließend wird ein Index erstellt, der jeder CSS-Regel eine Position in der Stylesheet-Quelle zuordnet.

Sie fragen sich vielleicht, warum das CSS noch einmal geparst werden muss. Das Problem dabei ist, dass sich der Browser aus Leistungsgründen nicht um die Quellpositionen von CSS-Regeln kümmert und sie daher nicht speichert. Für die CSS-Bearbeitung in den DevTools sind jedoch die Quellpositionen erforderlich. Wir möchten nicht, dass normale Chrome-Nutzer die Leistungseinbußen spüren, aber wir möchten, dass DevTools-Nutzer Zugriff auf die Quellpositionen haben. Dieser Ansatz zur erneuten Analyse eignet sich für beide Anwendungsfälle und hat nur minimale Nachteile.

Als Nächstes fordert die CSS.getMatchedStylesForNode-Implementierung die Style Engine des Browsers auf, CSS-Regeln für das angegebene Element bereitzustellen. Schließlich ordnet die Methode die von der Style Engine zurückgegebenen Regeln dem Quellcode zu und liefert eine strukturierte Antwort zu CSS-Regeln, damit DevTools weiß, welcher Teil der Regel der Selektor oder die Eigenschaften sind. So können Sie den Selektor und die Properties unabhängig voneinander in den Entwicklertools bearbeiten.

Sehen wir uns nun die Bearbeitung an. Denken Sie daran, dass CSS.getMatchedStylesForNode die Quellpositionen für jede Regel zurückgibt? Das ist für die Bearbeitung entscheidend. Wenn Sie eine Regel ändern, gibt DevTools einen weiteren CDP-Befehl aus, mit dem die Seite tatsächlich aktualisiert wird. Der Befehl enthält die ursprüngliche Position des zu aktualisierenden Regelfragments und den neuen Text, mit dem das Fragment aktualisiert werden soll.

Im Backend aktualisiert DevTools das Ziel-Stylesheet, wenn der Bearbeitungsaufruf verarbeitet wird. Außerdem aktualisiert er die Kopie der Stylesheet-Quelle, die er verwaltet, und die Quellpositionen für die aktualisierte Regel. Als Antwort auf den Bearbeitungsaufruf erhält das DevTools-Frontend die aktualisierten Positionen für das gerade aktualisierte Textfragment zurück.

Das erklärt, warum die Bearbeitung von CSS-in-JS in den DevTools nicht funktioniert hat: CSS-in-JS hat keine eigentliche Quelle, die irgendwo gespeichert ist, und die CSS-Regeln befinden sich im Speicher des Browsers in CSSOM-Datenstrukturen.

So haben wir CSS-in-JS unterstützt

Um das Bearbeiten von CSS-in-JS-Regeln zu unterstützen, haben wir uns daher dafür entschieden, eine Quelle für erstellte Stylesheets zu erstellen, die mit dem oben beschriebenen Mechanismus bearbeitet werden können.

Im ersten Schritt wird der Quelltext erstellt. Die Stil-Engine des Browsers speichert die CSS-Regeln in der Klasse CSSStyleSheet. Die Instanzen dieser Klasse können Sie wie bereits erwähnt in JavaScript erstellen. Der Code zum Erstellen des Quelltexts sieht so aus:

String InspectorStyleSheet::CollectStyleSheetRules() {
  StringBuilder builder;
  for (unsigned i = 0; i < page_style_sheet_->length(); i++) {
    builder.Append(page_style_sheet_->item(i)->cssText());
    builder.Append('\n');
  }
  return builder.ToString();
}

Er iteriert über die Regeln in einer CSSStyleSheet-Instanz und erstellt daraus einen einzelnen String. Diese Methode wird aufgerufen, wenn eine Instanz der Klasse „InspectorStyleSheet“ erstellt wird. Die Klasse „InspectorStyleSheet“ umschließt eine CSSStyleSheet-Instanz und extrahiert zusätzliche Metadaten, die für DevTools erforderlich sind:

void InspectorStyleSheet::UpdateText() {
  String text;
  bool success = InspectorStyleSheetText(&text);
  if (!success)
    success = InlineStyleSheetText(&text);
  if (!success)
    success = ResourceStyleSheetText(&text);
  if (!success)
    success = CSSOMStyleSheetText(&text);
  if (success)
    InnerSetText(text, false);
}

In diesem Snippet sehen wir CSSOMStyleSheetText, das CollectStyleSheetRules intern aufruft. CSSOMStyleSheetText wird aufgerufen, wenn das Stylesheet nicht inline oder ein Ressourcen-Stylesheet ist. Mit diesen beiden Snippets können Sie die mit dem new CSSStyleSheet()-Konstruktor erstellten Stylesheets bereits grundlegend bearbeiten.

Ein Sonderfall sind die Stylesheets, die mit einem <style>-Tag verknüpft sind und mit der CSSOM API mutiert wurden. In diesem Fall enthält das Stylesheet den Quelltext und zusätzliche Regeln, die in der Quelle nicht vorhanden sind. Um diesen Fall zu behandeln, führen wir eine Methode ein, mit der diese zusätzlichen Regeln in den Quelltext eingefügt werden. Die Reihenfolge ist hier wichtig, da CSS-Regeln mitten im ursprünglichen Quelltext eingefügt werden können. Angenommen, das ursprüngliche <style>-Element enthielt den folgenden Text:

/* comment */
.rule1 {}
.rule3 {}

Anschließend wurden mithilfe der JS API einige neue Regeln auf der Seite eingefügt, wodurch die folgende Reihenfolge der Regeln entstand: .rule0, .rule1, .rule2, .rule3, .rule4. Der resultierende Quelltext nach dem Zusammenführen sollte so aussehen:

.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}

Die Beibehaltung der ursprünglichen Kommentare und Einzüge ist für den Bearbeitungsprozess wichtig, da die Positionen der Regeln im Quelltext genau sein müssen.

Ein weiterer Aspekt, der für CSS-in-JS-Stylesheets besonders ist, ist, dass sie jederzeit von der Seite geändert werden können. Wenn die tatsächlichen CSSOM-Regeln nicht mehr mit der Textversion synchronisiert sind, funktioniert die Bearbeitung nicht. Dazu haben wir eine sogenannte Sonde eingeführt, mit der der Browser den Backend-Teil von DevTools benachrichtigen kann, wenn ein Stylesheet mutiert wird. Die mutierten Stylesheets werden dann beim nächsten Aufruf von CSS.getMatchedStylesForNode synchronisiert.

Mit all diesen Elementen funktioniert die CSS-in-JS-Bearbeitung bereits, aber wir wollten die Benutzeroberfläche verbessern, um anzuzeigen, ob ein Stylesheet erstellt wurde. Wir haben dem CDP-Attribut CSS.CSSStyleSheetHeader das neue Attribut isConstructed hinzugefügt, das vom Frontend verwendet wird, um die Quelle einer CSS-Regel korrekt anzuzeigen:

Erstellbares Stylesheet

Ergebnisse

Wir haben uns die relevanten Anwendungsfälle für CSS-in-JS angesehen, die in DevTools nicht unterstützt wurden, und die Lösung zur Unterstützung dieser Anwendungsfälle erläutert. Das Interessante an dieser Implementierung ist, dass wir vorhandene Funktionen nutzen konnten, indem wir CSSOM-CSS-Regeln einen regulären Quelltext hinzufügten. So musste die Stilbearbeitung in DevTools nicht komplett neu konzipiert werden.

Weitere Informationen finden Sie in unserem Designvorschlag oder im Chromium-Tracking-Fehler, der alle zugehörigen Patches enthält.

Vorschaukanäle herunterladen

Verwenden Sie als Standard-Entwicklungsbrowser Chrome Canary, Chrome Dev oder Chrome Beta. Diese Vorabversionen bieten Ihnen Zugriff auf die neuesten DevTools-Funktionen, ermöglichen es Ihnen, innovative Webplattform-APIs zu testen, und helfen Ihnen, Probleme auf Ihrer Website zu finden, bevor Ihre Nutzer sie bemerken.

Chrome-Entwicklertools-Team kontaktieren

Mit den folgenden Optionen können Sie über neue Funktionen, Updates oder andere Themen im Zusammenhang mit den DevTools sprechen.