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. Dies könnte beispielsweise bedeuten, dass der CSS-Inhalt mithilfe von JavaScript definiert und die endgültige CSS-Ausgabe in der App generiert wird.

Im Zusammenhang mit Entwicklertools bedeutet „CSS-in-JS“, dass die CSS-Inhalte mithilfe von CSSOM APIs in die Seite eingeschleust werden. Reguläres CSS wird mithilfe von <style>- oder <link>-Elementen eingeschleust und hat eine statische Quelle (z.B. einen DOM-Knoten oder eine Netzwerkressource). CSS-in-JS hingegen hat oft keine statische Quelle. Ein Sonderfall hier ist, dass der Inhalt eines <style>-Elements mithilfe der CSSOM API aktualisiert werden kann, wodurch die Quelle nicht mit dem tatsächlichen CSS-Stylesheet synchron 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 mithilfe der CSSOM API einfügen können, ähnlich wie CSS-in-JS-Bibliotheken.

// 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 den Entwicklertools

Die am häufigsten verwendete Funktion in den Entwicklertools ist der Bereich Styles (Stile). 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 mithilfe der CSSOM APIs geändert wurden, eher eingeschränkt: Sie konnten nur die angewendeten Regeln 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 nennen wir CSS-in-JS-Stile auch "built", um anzugeben, dass sie mit Web APIs erstellt wurden.

Sehen wir uns die Bearbeitung von Stilen in den Entwicklertools genauer an.

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 reguläre Chrome-Nutzer die Leistungseinbußen zahlen, aber wir möchten, dass die Entwicklertools-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 Stil-Engine des Browsers auf, CSS-Regeln bereitzustellen, die mit dem jeweiligen Element übereinstimmen. 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 wichtig für die Bearbeitung. Wenn Sie eine Regel ändern, sendet DevTools einen weiteren CDP-Befehl, der die Seite tatsächlich aktualisiert. 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 die Entwicklertools 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 handhaben, führen wir eine Methode ein, mit der diese zusätzlichen Regeln im Quelltext zusammengeführt werden können. Hier ist die Reihenfolge 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 Quelltext nach der Zusammenführung sollte wie folgt lauten:

.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

Schlussfolgerungen

Um unsere Geschichte zusammenzufassen, haben wir uns die relevanten Anwendungsfälle im Zusammenhang mit CSS-in-JS angesehen, die von den Entwicklertools nicht unterstützt wurden, und die Lösung für diese Anwendungsfälle durchgegangen. 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. Über diese Vorschaukanäle erhältst du Zugriff auf die neuesten Entwicklertools, kannst hochmoderne Webplattform-APIs testen und Probleme auf deiner Website erkennen, bevor deine Nutzer dies tun.

Chrome-Entwicklertools-Team kontaktieren

Verwende die folgenden Optionen, um die neuen Funktionen, Updates oder andere Aspekte der Entwicklertools zu besprechen.