Modernisering van de CSS-infrastructuur in DevTools

Vernieuwing van de DevTools-architectuur: modernisering van de CSS-infrastructuur in DevTools

Dit bericht maakt deel uit van een reeks blogposts waarin de wijzigingen worden beschreven die we aanbrengen in de architectuur van DevTools en hoe deze is gebouwd. We zullen uitleggen hoe CSS historisch in DevTools werkte en hoe we onze CSS in DevTools hebben gemoderniseerd ter voorbereiding op (uiteindelijk) de migratie naar een webstandaardoplossing voor het laden van CSS in JavaScript-bestanden.

Vorige staat van CSS in DevTools

DevTools implementeerde CSS op twee verschillende manieren: één voor CSS-bestanden die worden gebruikt in het oudere deel van DevTools, en één voor de moderne webcomponenten die in DevTools worden gebruikt.

De CSS-implementatie in DevTools is vele jaren geleden gedefinieerd en is nu verouderd. DevTools blijft vasthouden aan het module.json -patroon en er is veel moeite gedaan om deze bestanden te verwijderen. De laatste blokkering voor het verwijderen van deze bestanden is de resources , die wordt gebruikt om CSS-bestanden te laden.

We wilden tijd besteden aan het verkennen van verschillende mogelijke oplossingen die uiteindelijk zouden kunnen veranderen in CSS-modulescripts . Het doel was om de technische schulden veroorzaakt door het oude systeem weg te nemen, maar ook om het migratieproces naar CSS-modulescripts eenvoudiger te maken.

Alle CSS-bestanden die zich in DevTools bevonden, werden als 'legacy' beschouwd omdat ze werden geladen met behulp van een module.json -bestand, dat momenteel wordt verwijderd. Alle CSS-bestanden moesten worden vermeld onder resources in een module.json -bestand in dezelfde map als het CSS-bestand.

Een voorbeeld van een resterend module.json -bestand:

{
  "resources": [
    "serviceWorkersView.css",
    "serviceWorkerUpdateCycleView.css"
  ]
}

Deze CSS-bestanden vullen vervolgens een globale objectkaart in met de naam Root.Runtime.cachedResources als een toewijzing van een pad naar hun inhoud. Om stijlen aan DevTools toe te voegen, moet u registerRequiredCSS aanroepen met het exacte pad naar het bestand dat u wilt laden.

Voorbeeld registerRequiredCSS -oproep :

constructor() {
  …
  this.registerRequiredCSS('ui/legacy/components/quick_open/filteredListWidget.css');
  …
}

Hiermee wordt de inhoud van het CSS-bestand opgehaald en als <style> -element in de pagina ingevoegd met behulp van de appendStyle functie:.

appendStyle functie die CSS toevoegt met behulp van een inline stijlelement:

const content = Root.Runtime.cachedResources.get(cssFile) || '';

if (!content) {
  console.error(cssFile + ' not preloaded. Check module.json');
}

const styleElement = document.createElement('style');
styleElement.textContent = content;
node.appendChild(styleElement);

Toen we moderne webcomponenten introduceerden (met behulp van aangepaste elementen), besloten we in eerste instantie CSS te gebruiken via inline <style> tags in de componentbestanden zelf. Dit bracht zijn eigen uitdagingen met zich mee:

  • Gebrek aan ondersteuning voor syntaxisaccentuering. Plug-ins die syntaxisaccentuering bieden voor inline CSS zijn doorgaans niet zo goed als de functies voor syntaxisaccentuering en automatisch aanvullen voor CSS die in .css bestanden is geschreven.
  • Prestatie-overhead opbouwen. Inline CSS betekende ook dat er twee passages nodig waren voor linting: één voor CSS-bestanden en één voor inline CSS. Dit was een prestatieoverhead die we konden verwijderen als alle CSS in zelfstandige CSS-bestanden was geschreven.
  • Uitdaging in minificatie. Inline CSS kon niet gemakkelijk worden verkleind, dus geen enkele CSS werd verkleind. De bestandsgrootte van de release-build van DevTools werd ook vergroot door de dubbele CSS die werd geïntroduceerd door meerdere exemplaren van dezelfde webcomponent.

Het doel van mijn stageproject was het vinden van een oplossing voor de CSS-infrastructuur die werkt met zowel de bestaande infrastructuur als de nieuwe webcomponenten die in DevTools worden gebruikt.

Onderzoeken van mogelijke oplossingen

Het probleem kan in twee verschillende delen worden opgesplitst:

  • Uitzoeken hoe het bouwsysteem omgaat met CSS-bestanden.
  • Uitzoeken hoe de CSS-bestanden worden geïmporteerd en gebruikt door DevTools.

Voor elk onderdeel hebben we verschillende mogelijke oplossingen bekeken. Deze worden hieronder beschreven.

CSS-bestanden importeren

Het doel van het importeren en gebruiken van CSS in de TypeScript-bestanden was om zo dicht mogelijk bij de webstandaarden te blijven, consistentie in DevTools af te dwingen en dubbele CSS in onze HTML te vermijden . We wilden ook een oplossing kunnen kiezen die het mogelijk zou maken om onze wijzigingen te migreren naar nieuwe webplatformstandaarden, zoals CSS Module Scripts.

Om deze redenen zijn de @import- instructies en tags leken niet de juiste keuze voor DevTools. Ze zouden niet uniform zijn met de import in de rest van DevTools en resulteren in een Flash Of Unstyled Content (FOUC) . De migratie naar CSS-modulescripts zou moeilijker zijn omdat de import expliciet zou moeten worden toegevoegd en anders zou moeten worden afgehandeld dan met <link> -tags.

const output = LitHtml.html`
<style> @import "css/styles.css"; </style>
<button> Hello world </button>`
const output = LitHtml.html`
<link rel="stylesheet" href="styles.css">
<button> Hello World </button>`

Mogelijke oplossingen met @import of <link> .

In plaats daarvan hebben we ervoor gekozen om een ​​manier te vinden om het CSS-bestand te importeren als een CSSStyleSheet object, zodat we het kunnen toevoegen aan de Shadow Dom (DevTools gebruikt Shadow DOM nu al een paar jaar) met behulp van de adoptedStyleSheets eigenschap.

Bundelopties

We hadden een manier nodig om CSS-bestanden naar een CSSStyleSheet object te converteren, zodat we het gemakkelijk in het TypeScript-bestand konden manipuleren. We hebben zowel Rollup als webpack overwogen als potentiële bundelaars om deze transformatie voor ons uit te voeren. DevTools gebruikt Rollup al in zijn productiebuild, maar het toevoegen van een van beide bundels aan de productiebuild kan potentiële prestatieproblemen opleveren bij het werken met ons huidige buildsysteem. Onze integratie met het GN-bouwsysteem van Chromium maakt bundelen lastiger en daarom hebben bundelaars de neiging niet goed te integreren met het huidige Chromium-bouwsysteem.

In plaats daarvan hebben we de optie onderzocht om het huidige GN-bouwsysteem te gebruiken om deze transformatie voor ons uit te voeren.

De nieuwe infrastructuur voor het gebruik van CSS in DevTools

De nieuwe oplossing omvat het gebruik van adoptedStyleSheets om stijlen toe te voegen aan een bepaalde Shadow DOM, terwijl het GN-buildsysteem wordt gebruikt om CSSStyleSheet-objecten te genereren die kunnen worden overgenomen door een document of een ShadowRoot .

// CustomButton.ts

// Import the CSS style sheet contents from a JS file generated from CSS
import customButtonStyles from './customButton.css.js';
import otherStyles from './otherStyles.css.js';

export class CustomButton extends HTMLElement{
  …
  connectedCallback(): void {
    // Add the styles to the shadow root scope
    this.shadow.adoptedStyleSheets = [customButtonStyles, otherStyles];
  }
}

Het gebruik adoptedStyleSheets heeft meerdere voordelen, waaronder:

  • Het is bezig een moderne webstandaard te worden
  • Voorkomt dubbele CSS
  • Past stijlen alleen toe op een Shadow DOM en dit vermijdt problemen veroorzaakt door dubbele klassenamen of ID-kiezers in CSS-bestanden
  • Eenvoudig te migreren naar toekomstige webstandaarden zoals CSS Module Scripts en Import Assertions

Het enige voorbehoud bij de oplossing was dat de import vereisten dat het .css.js bestand moest worden geïmporteerd. Om GN tijdens het bouwen een CSS-bestand te laten genereren, hebben we het script generate_css_js_files.js geschreven. Het bouwsysteem verwerkt nu elk CSS-bestand en transformeert het naar een JavaScript-bestand dat standaard een CSSStyleSheet object exporteert. Dit is geweldig omdat we het CSS-bestand kunnen importeren en gemakkelijk kunnen overnemen. Bovendien kunnen we de productiebuild nu ook eenvoudig verkleinen, waardoor de bestandsgrootte wordt bespaard:

const styles = new CSSStyleSheet();
styles.replaceSync(
  // In production, we also minify our CSS styles
  /`${isDebug ? output : cleanCSS.minify(output).styles}
  /*# sourceURL=${fileName} */`/
);

export default styles;

Voorbeeld gegenereerd iconButton.css.js uit het script.

Verouderde code migreren met behulp van ESLint-regels

Hoewel de webcomponenten eenvoudig handmatig konden worden gemigreerd, was het proces voor het migreren van verouderd gebruik van registerRequiredCSS ingewikkelder. De twee belangrijkste functies die oudere stijlen registreerden, waren registerRequiredCSS en createShadowRootWithCoreStyles . Omdat de stappen om deze oproepen te migreren redelijk mechanisch waren, besloten we dat we ESLint-regels konden gebruiken om oplossingen toe te passen en verouderde code automatisch te migreren. DevTools gebruikt al een aantal aangepaste regels die specifiek zijn voor de DevTools-codebase. Dit was nuttig omdat ESLint de code al parseert in een abstracte syntaxisboom (afgekort AST) en we de specifieke oproepknooppunten konden opvragen die oproepen waren voor het registreren van CSS.

Het grootste probleem waarmee we te maken kregen bij het schrijven van de ESLint-regels voor de migratie was het vastleggen van randgevallen. We wilden ervoor zorgen dat we de juiste balans vonden tussen weten welke edge cases de moeite waard waren om vast te leggen en welke handmatig gemigreerd moesten worden. We wilden er ook voor kunnen zorgen dat we een gebruiker konden vertellen wanneer een geïmporteerd .css.js bestand niet automatisch door het buildsysteem wordt gegenereerd, omdat dit voorkomt dat er tijdens runtime fouten in het bestand niet gevonden worden.

Een nadeel van het gebruik van ESLint-regels voor de migratie was dat we het vereiste GN-buildbestand in het systeem niet konden wijzigen. Deze wijzigingen moesten handmatig door de gebruiker in elke map worden aangebracht. Hoewel dit meer werk vergde, was het een goede manier om te bevestigen dat elk .css.js bestand dat werd geïmporteerd daadwerkelijk door het bouwsysteem werd gegenereerd.

Over het geheel genomen was het gebruik van ESLint-regels voor deze migratie erg nuttig, omdat we de oude code snel naar de nieuwe infrastructuur konden migreren en omdat de AST direct beschikbaar was, konden we ook meerdere randgevallen in de regel afhandelen en deze op betrouwbare wijze automatisch repareren met behulp van de fixer van ESLint API .

Wat nu?

Tot nu toe zijn alle webcomponenten in Chromium DevTools gemigreerd om de nieuwe CSS-infrastructuur te gebruiken in plaats van inline stijlen te gebruiken. De meeste oude toepassingen van registerRequiredCSS zijn ook gemigreerd om het nieuwe systeem te gebruiken. Het enige dat overblijft is om zoveel mogelijk module.json bestanden te verwijderen en vervolgens deze huidige infrastructuur te migreren om in de toekomst CSS-modulescripts te implementeren!

Download de voorbeeldkanalen

Overweeg het gebruik van Chrome Canary , Dev of Beta als uw standaard ontwikkelingsbrowser. Deze preview-kanalen geven u toegang tot de nieuwste DevTools-functies, testen geavanceerde webplatform-API's en ontdekken problemen op uw site voordat uw gebruikers dat doen!

Neem contact op met het Chrome DevTools-team

Gebruik de volgende opties om de nieuwe functies en wijzigingen in het bericht te bespreken, of iets anders gerelateerd aan DevTools.

  • Stuur ons een suggestie of feedback via crbug.com .
  • Rapporteer een DevTools-probleem met behulp van de opties MeerMeer > Help > Rapporteer een DevTools-probleem in DevTools.
  • Tweet op @ChromeDevTools .
  • Laat reacties achter op onze Wat is er nieuw in DevTools YouTube-video's of DevTools Tips YouTube-video's .