CSS-in-JS-ondersteuning in DevTools

Alex Rudenko
Alex Rudenko

Dit artikel gaat over CSS-in-JS-ondersteuning in DevTools die sinds Chrome 85 is geïntroduceerd en, in het algemeen, wat we bedoelen met CSS-in-JS en hoe dit verschilt van gewone CSS die al lange tijd door DevTools wordt ondersteund.

Wat is CSS-in-JS?

De definitie van CSS-in-JS is nogal vaag. In brede zin is het een benadering voor het beheren van CSS-code met behulp van JavaScript. Het kan bijvoorbeeld betekenen dat de CSS-inhoud wordt gedefinieerd met behulp van JavaScript en dat de uiteindelijke CSS-uitvoer direct door de app wordt gegenereerd.

In de context van DevTools betekent CSS-in-JS dat de CSS-inhoud in de pagina wordt geïnjecteerd met behulp van CSSOM API's . Reguliere CSS wordt geïnjecteerd met behulp van <style> - of <link> -elementen, en heeft een statische bron (bijvoorbeeld een DOM-knooppunt of een netwerkbron). Daarentegen heeft CSS-in-JS vaak geen statische bron. Een speciaal geval hier is dat de inhoud van een <style> -element kan worden bijgewerkt met behulp van de CSSOM API, waardoor de bron niet meer synchroon loopt met de daadwerkelijke CSS-stylesheet.

Als u een CSS-in-JS-bibliotheek gebruikt (bijvoorbeeld styled-component , Emotion , JSS ), kan de bibliotheek stijlen injecteren met behulp van CSSOM API's onder de motorkap, afhankelijk van de ontwikkelingsmodus en de browser.

Laten we eens kijken naar enkele voorbeelden van hoe u een stylesheet kunt injecteren met behulp van de CSSOM API, vergelijkbaar met wat CSS-in-JS-bibliotheken doen.

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

U kunt ook een volledig nieuw stylesheet maken :

// 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-ondersteuning in DevTools

In DevTools is het deelvenster Stijlen de meest gebruikte functie bij het omgaan met CSS. In het deelvenster Stijlen kunt u bekijken welke regels van toepassing zijn op een bepaald element en kunt u de regels bewerken en de wijzigingen in realtime op de pagina zien.

Vóór vorig jaar was de ondersteuning voor CSS-regels die waren aangepast met behulp van CSSOM API's nogal beperkt: je kon alleen de toegepaste regels zien, maar niet bewerken. Het belangrijkste doel dat we vorig jaar hadden, was het bewerken van CSS-in-JS-regels mogelijk te maken met behulp van het deelvenster Stijlen. Soms noemen we CSS-in-JS-stijlen ook 'geconstrueerd' om aan te geven dat ze zijn geconstrueerd met behulp van web-API's.

Laten we eens kijken naar de details van het bewerken van stijlen in DevTools.

Stijlbewerkingsmechanisme in DevTools

Stijlbewerkingsmechanisme in DevTools

Wanneer u een element in DevTools selecteert, wordt het deelvenster Stijlen weergegeven. Het deelvenster Stijlen geeft een CDP-opdracht uit met de naam CSS.getMatchedStylesForNode om CSS-regels op te halen die op het element van toepassing zijn. CDP staat voor Chrome DevTools Protocol en het is een API waarmee de frontend van DevTools aanvullende informatie over de geïnspecteerde pagina kan krijgen.

Wanneer het wordt aangeroepen, identificeert CSS.getMatchedStylesForNode alle stylesheets in het document en parseert deze met behulp van de CSS-parser van de browser. Vervolgens bouwt het een index op die elke CSS-regel associeert met een positie in de stylesheetbron.

Je zou je kunnen afvragen: waarom moet de CSS opnieuw worden geparseerd? Het probleem hier is dat de browser om prestatieredenen zich niet bezighoudt met de bronposities van CSS-regels en deze daarom niet opslaat. Maar DevTools heeft de bronposities nodig om CSS-bewerking te ondersteunen. We willen niet dat gewone Chrome-gebruikers de prestatieboete betalen, maar we willen wel dat DevTools-gebruikers toegang hebben tot de bronposities. Deze re-parsing-aanpak richt zich op beide gebruiksscenario's met minimale nadelen.

Vervolgens vraagt ​​de CSS.getMatchedStylesForNode -implementatie de stijlengine van de browser om CSS-regels te leveren die overeenkomen met het gegeven element. En ten slotte associeert de methode de door de stijlengine geretourneerde regels met de broncode en biedt een gestructureerd antwoord over CSS-regels, zodat DevTools weet welk deel van de regel de selector of eigenschappen is. Hiermee kan DevTools de selector en de eigenschappen onafhankelijk bewerken.

Laten we nu eens kijken naar het bewerken. Weet je nog dat CSS.getMatchedStylesForNode bronposities retourneert voor elke regel? Dat is cruciaal voor het bewerken. Wanneer u een regel wijzigt, geeft DevTools nog een CDP-opdracht uit die de pagina daadwerkelijk bijwerkt. Het commando bevat de oorspronkelijke positie van het fragment van de regel dat wordt bijgewerkt en de nieuwe tekst waarmee het fragment moet worden bijgewerkt.

Op de backend werkt DevTools bij het afhandelen van de bewerkingsaanroep het doelstylesheet bij. Ook wordt de kopie bijgewerkt van de stylesheetbron die wordt bijgehouden, en worden de bronposities voor de bijgewerkte regel bijgewerkt. Als reactie op de bewerkingsaanroep krijgt de DevTools-frontend de bijgewerkte posities terug voor het tekstfragment dat zojuist is bijgewerkt.

Dit verklaart waarom het bewerken van CSS-in-JS in DevTools niet uit de doos werkte: CSS-in-JS heeft nergens een daadwerkelijke bron opgeslagen en de CSS-regels leven in het geheugen van de browser in CSSOM-gegevensstructuren .

Hoe we ondersteuning voor CSS-in-JS hebben toegevoegd

Om het bewerken van CSS-in-JS-regels te ondersteunen, hebben we daarom besloten dat de beste oplossing zou zijn om een ​​bron voor geconstrueerde stylesheets te maken die kunnen worden bewerkt met behulp van het bestaande mechanisme dat hierboven is beschreven.

De eerste stap is het opbouwen van de brontekst. De stijlengine van de browser slaat de CSS-regels op in de klasse CSSStyleSheet . Die klasse is degene waarvan u de instanties kunt maken vanuit JavaScript, zoals eerder besproken. De code om de brontekst samen te stellen is als volgt:

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

Het herhaalt de regels die in een CSSStyleSheet-instantie voorkomen en bouwt er een enkele string uit. Deze methode wordt aangeroepen wanneer een exemplaar van de klasse InspectorStyleSheet wordt gemaakt. De klasse InspectorStyleSheet omhult een CSSStyleSheet-instantie en extraheert aanvullende metagegevens die vereist zijn door DevTools:

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 dit fragment zien we CSSOMStyleSheetText dat CollectStyleSheetRules intern aanroept. CSSOMStyleSheetText wordt aangeroepen als de stylesheet niet inline of een resource-stylesheet is. In principe maken deze twee fragmenten al basisbewerking mogelijk van de stylesheets die zijn gemaakt met behulp van de new CSSStyleSheet() constructor.

Een speciaal geval zijn de stylesheets die zijn gekoppeld aan een <style> -tag en die zijn gemuteerd met behulp van de CSSOM API. In dit geval bevat het stylesheet de brontekst en aanvullende regels die niet in de bron aanwezig zijn. Om dit geval aan te pakken, introduceren we een methode om deze aanvullende regels in de brontekst samen te voegen. Hier is de volgorde van belang omdat CSS-regels in het midden van de originele brontekst kunnen worden ingevoegd. Stel je bijvoorbeeld voor dat het originele <style> -element de volgende tekst bevatte:

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

Vervolgens voegde de pagina een aantal nieuwe regels in met behulp van de JS API, waardoor de volgende volgorde van regels ontstond: .rule0, .rule1, .rule2, .rule3, .rule4. De resulterende brontekst na de samenvoegbewerking zou er als volgt uit moeten zien:

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

Het behoud van de originele opmerkingen en inspringingen is belangrijk voor het bewerkingsproces, omdat de brontekstposities van regels nauwkeurig moeten zijn.

Een ander aspect dat speciaal is voor CSS-in-JS stylesheets is dat ze op elk moment door de pagina kunnen worden gewijzigd . Als de daadwerkelijke CSSOM-regels niet synchroon zouden lopen met de tekstversie, zou de bewerking niet werken. Hiervoor hebben we een zogenaamde probe geïntroduceerd, waarmee de browser het backend-gedeelte van DevTools op de hoogte kan stellen wanneer een stylesheet wordt gemuteerd. Gemuteerde stylesheets worden vervolgens gesynchroniseerd tijdens de volgende aanroep van CSS.getMatchedStylesForNode.

Nu al deze onderdelen aanwezig zijn, werkt CSS-in-JS-bewerking al, maar we wilden de gebruikersinterface verbeteren om aan te geven of er een stylesheet is gemaakt. We hebben een nieuw attribuut met de naam isConstructed toegevoegd aan CDP's CSS.CSSStyleSheetHeader , waar de frontend gebruik van maakt om de bron van een CSS-regel correct weer te geven:

Constructeerbaar stijlblad

Conclusies

Om ons verhaal hier samen te vatten, hebben we de relevante gebruiksscenario's met betrekking tot CSS-in-JS doorgenomen die DevTools niet ondersteunde, en hebben we de oplossing doorgenomen om die gebruiksscenario's te ondersteunen. Het interessante deel van deze implementatie is dat we bestaande functionaliteit konden benutten door CSSOM CSS-regels een reguliere brontekst te geven, waardoor de noodzaak om de stijlbewerking in DevTools volledig opnieuw te ontwerpen werd vermeden.

Bekijk voor meer achtergrondinformatie ons ontwerpvoorstel of de Chromium- trackingbug die verwijst naar alle gerelateerde patches.

Download de voorbeeldkanalen

Overweeg om Chrome Canary , Dev of Beta te gebruiken als uw standaard ontwikkelingsbrowser. Met deze voorbeeldkanalen krijgt u toegang tot de nieuwste DevTools-functies, kunt u geavanceerde webplatform-API's testen en kunt u problemen op uw site opsporen voordat uw gebruikers dat doen!

Neem contact op met het Chrome DevTools-team

Gebruik de volgende opties om de nieuwe functies, updates of iets anders gerelateerd aan DevTools te bespreken.