Questo articolo parla del supporto di CSS in JS in DevTools, disponibile da Chrome 85, e, in generale, di cosa si intende per CSS in JS e di come si differenzia dal CSS normale supportato da DevTools da molto tempo.
Che cos'è CSS-in-JS?
La definizione di CSS-in-JS è piuttosto vaga. In senso ampio, si tratta di un approccio alla gestione del codice CSS mediante JavaScript. Ad esempio, potrebbe significare che i contenuti CSS sono definiti utilizzando JavaScript e l'output CSS finale viene generato dinamicamente dall'app.
Nel contesto di DevTools, il comando CSS-in-JS indica che i contenuti CSS vengono inseriti nella pagina utilizzando le API CSSOM. Il CSS normale viene inserito utilizzando elementi <style>
o <link>
e ha un'origine statica (ad esempio un nodo DOM o una risorsa di rete). Al contrario, CSS-in-JS spesso non ha un'origine statica. Un caso speciale è che i contenuti di un elemento <style>
possono essere aggiornati utilizzando l'API CSSOM, causando la mancata sincronizzazione dell'origine con il foglio di stile CSS effettivo.
Se utilizzi una libreria CSS in JS (ad es. componente con stile, Emotion, JSS), la libreria potrebbe inserire stili utilizzando le API CSSOM in background, a seconda della modalità di sviluppo e del browser.
Vediamo alcuni esempi di come puoi iniettare uno stile utilizzando l'API CSSOM in modo simile a quanto fanno le librerie CSS-in-JS.
// 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; }');
Puoi anche creare un foglio di stile completamente nuovo:
// 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];
Supporto CSS in DevTools
In DevTools, la funzionalità più utilizzata quando si tratta di CSS è il riquadro Stili. Nel riquadro Stili, puoi visualizzare le regole applicate a un determinato elemento, modificarle e vedere le modifiche nella pagina in tempo reale.
Prima dell'anno scorso, il supporto delle regole CSS modificate utilizzando le API CSSOM era piuttosto limitato: potevi vedere solo le regole applicate, ma non modificarle. L'obiettivo principale che ci eravamo prefissati l'anno scorso era consentire la modifica delle regole CSS-in-JS utilizzando il riquadro Stili. A volte chiamiamo gli stili CSS-in-JS anche "costruiti" per indicare che sono stati creati utilizzando API web.
Approfondiamo i dettagli del funzionamento dell'editing degli stili in DevTools.
Meccanismo di modifica dello stile in DevTools
Quando selezioni un elemento in DevTools, viene visualizzato il riquadro Stili. Il riquadro Stili invia un comando CDP denominato CSS.getMatchedStylesForNode per ottenere le regole CSS applicabili all'elemento. CDP è l'acronimo di Chrome DevTools Protocol; è un'API che consente al frontend di DevTools di ottenere ulteriori informazioni sulla pagina ispezionata.
Quando viene richiamato, CSS.getMatchedStylesForNode
identifica tutti gli stili nel documento e li analizza utilizzando il parser CSS del browser. Quindi, crea un indice che associa ogni regola CSS a una posizione nell'origine dello stile.
Potresti chiederti perché è necessario analizzare di nuovo il CSS. Il problema è che, per motivi di prestazioni, il browser stesso non si occupa delle posizioni di origine delle regole CSS e, di conseguenza, non le memorizza. Tuttavia, DevTools ha bisogno delle posizioni di origine per supportare la modifica CSS. Non vogliamo che gli utenti normali di Chrome paghino la penalizzazione del rendimento, ma vogliamo che gli utenti di DevTools abbiano accesso alle posizioni dell'origine. Questo approccio di analisi di nuovo soddisfa entrambi i casi d'uso con svantaggi minimi.
Successivamente, l'implementazione di CSS.getMatchedStylesForNode
chiede al motore degli stili del browser di fornire le regole CSS corrispondenti all'elemento specificato. Infine, il metodo associa le regole restituite dall'engine dello stile al codice sorgente e fornisce una risposta strutturata sulle regole CSS in modo che DevTools sappia quale parte della regola è il selettore o le proprietà. Consente a DevTools di modificare il selettore e le proprietà in modo indipendente.
Ora passiamo all'editing. Ricordi che CSS.getMatchedStylesForNode
restituisce le posizioni delle origini per ogni regola? Questo è fondamentale per l'editing. Quando modifichi una regola, DevTools emette un altro comando CDP che aggiorna effettivamente la pagina. Il comando include la posizione originale del frammento della regola che viene aggiornato e il nuovo testo con cui deve essere aggiornato il frammento.
Sul backend, durante la gestione della chiamata di modifica, DevTools aggiorna lo stile CSS di destinazione. Inoltre, aggiorna la copia dell'origine del foglio di stile che conserva e aggiorna le posizioni di origine per la regola aggiornata. In risposta alla chiamata di modifica, il frontend di DevTools restituisce le posizioni aggiornate per il frammento di testo appena modificato.
Questo spiega perché la modifica di CSS-in-JS in DevTools non ha funzionato immediatamente: CSS-in-JS non ha un'origine effettiva archiviata da nessuna parte e le regole CSS si trovano nella memoria del browser nelle strutture di dati CSSOM.
Come abbiamo aggiunto il supporto di CSS-in-JS
Pertanto, per supportare la modifica di regole CSS-in-JS, abbiamo deciso che la soluzione migliore sarebbe creare un'origine per i fogli di stile creati che possono essere modificati utilizzando il meccanismo esistente descritto sopra.
Il primo passaggio consiste nel creare il testo di origine. Il motore degli stili del browser memorizza le regole CSS nella classe CSSStyleSheet
. Questa è la classe di cui puoi creare istanze da JavaScript, come discusso in precedenza. Il codice per creare il testo di origine è il seguente:
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();
}
Esegue l'iterazione sulle regole trovate in un'istanza CSSStyleSheet e ne crea una singola stringa. Questo metodo viene richiamato quando viene creata un'istanza della classe InspectorStyleSheet. La classe InspectorStyleSheet racchiude un'istanza CSSStyleSheet ed estrae metadati aggiuntivi richiesti da 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 questo snippet, vediamo CSSOMStyleSheetText
che chiama CollectStyleSheetRules
internamente. CSSOMStyleSheetText
viene invocato se lo stile non è in linea o se si tratta di uno stile delle risorse. In sostanza, questi due snippet consentono già la modifica di base degli stili di pagina creati utilizzando il costruttore new CSSStyleSheet()
.
Un caso speciale sono i fogli di stile associati a un tag <style>
che sono stati sottoposti a mutazione utilizzando l'API CSSOM. In questo caso, il foglio di stile contiene il testo di origine e regole aggiuntive non presenti nell'origine. Per gestire questo caso, abbiamo introdotto un metodo per unire queste regole aggiuntive al testo di origine. In questo caso l'ordine è importante perché le regole CSS possono essere inserite nel mezzo del testo sorgente originale. Ad esempio, immagina che l'elemento <style>
originale contenga il seguente testo:
/* comment */
.rule1 {}
.rule3 {}
Poi la pagina ha inserito alcune nuove regole utilizzando l'API JS, producendo il seguente ordine di regole: .rule0, .rule1, .rule2, .rule3, .rule4. Il testo di origine risultante dopo l'operazione di unione dovrebbe essere il seguente:
.rule0 {}
/* comment */
.rule1 {}
.rule2 {}
.rule3 {}
.rule4 {}
La conservazione dei commenti e dell'a capo originali è importante per il processo di modifica perché le posizioni del testo di origine delle regole devono essere precise.
Un altro aspetto speciale degli stili CSS-in-JS è che possono essere modificati dalla pagina in qualsiasi momento. Se le regole CSSOM effettive non fossero più sincronizzate con la versione di testo, la modifica non funzionerebbe. Per questo abbiamo introdotto un cosiddetto probe, che consente al browser di notificare la parte del backend di DevTools quando un foglio di stile viene modificato. Le stylesheet mutate vengono poi sincronizzate durante la chiamata successiva a CSS.getMatchedStylesForNode.
Con tutti questi elementi in atto, la modifica CSS-in-JS funziona già, ma volevamo migliorare l'interfaccia utente per indicare se è stato creato uno stile. Abbiamo aggiunto un nuovo attributo denominato isConstructed
a CSS.CSSStyleSheetHeader di CDP, di cui il frontend fa uso per visualizzare correttamente l'origine di una regola CSS:
Conclusioni
Per ricapitolare, abbiamo analizzato i casi d'uso pertinenti relativi a CSS-in-JS non supportati da DevTools e abbiamo esaminato la soluzione a supporto di questi casi d'uso. La parte interessante di questa implementazione è che siamo riusciti a sfruttare la funzionalità esistente facendo in modo che le regole CSSOM abbiano un testo di origine regolare, evitando la necessità di riorganizzare completamente la modifica degli stili in DevTools.
Per maggiori informazioni, leggi la nostra proposta di progettazione o il bug di monitoraggio di Chromium che fa riferimento a tutte le patch correlate.
Scaricare i canali di anteprima
Valuta la possibilità di utilizzare Chrome Canary, Dev o Beta come browser di sviluppo predefinito. Questi canali di anteprima ti consentono di accedere alle funzionalità più recenti di DevTools, di testare API di piattaforme web all'avanguardia e di trovare i problemi sul tuo sito prima che lo facciano gli utenti.
Contatta il team di Chrome DevTools
Utilizza le seguenti opzioni per discutere di nuove funzionalità, aggiornamenti o qualsiasi altro argomento relativo a DevTools.
- Inviaci il tuo feedback e le richieste di funzionalità all'indirizzo crbug.com.
- Segnala un problema di DevTools utilizzando Altre opzioni > Guida > Segnala un problema di DevTools in DevTools.
- Invia un tweet all'account @ChromeDevTools.
- Lascia un commento sui video di YouTube Novità di DevTools o sui video di YouTube Suggerimenti per DevTools.