Im April letzten Jahres stellte Facebook sein großes neues Redesign vor. Ein ehrgeiziges Projekt, es war ein Relaunch einer großen Website mit einer riesigen Nutzerzahl. Um dies zu erreichen, nutzten sie mehrere Technologien, die sie selbst entwickelt und als Open Source veröffentlicht haben, wie React, GraphQL, Relay und eine neue CSS-in-JS-Bibliothek namens stylex.
Diese neue Bibliothek ist intern bei Facebook, aber sie haben genug Informationen darüber geteilt, um eine Open-Source-Implementierung namens style9 zu ermöglichen.
Warum eine weitere CIJ-Bibliothek?
Es gibt bereits viele CSS-in-JS (CIJ)-Bibliotheken, daher ist es vielleicht nicht offensichtlich, warum eine weitere benötigt wird. style9 bietet die gleichen Vorteile wie alle anderen CIJ-Lösungen, wie von Christopher Chedeau dargelegt, darunter gescoped Selektoren, Eliminierung von totem Code, deterministische Auflösung und die Möglichkeit, Werte zwischen CSS und JavaScript zu teilen.
Es gibt jedoch einige Dinge, die style9 einzigartig machen.
Minimaler Laufzeit-Code
Obwohl die Stile in JavaScript definiert sind, werden sie vom Compiler in eine normale CSS-Datei extrahiert. Das bedeutet, dass keine Stile in Ihrer endgültigen JavaScript-Datei ausgeliefert werden. Übrig bleiben nur die finalen Klassennamen, die der minimale Laufzeit-Code bedingt anwendet, genau wie Sie es normalerweise tun würden. Dies führt zu kleineren Code-Bundles, einer Reduzierung des Speicherverbrauchs und schnellerem Rendering.
Da die Werte zur Kompilierungszeit extrahiert werden, können dynamische Werte nicht verwendet werden. Diese sind glücklicherweise nicht sehr verbreitet und da sie einzigartig sind, leiden sie nicht darunter, inline definiert zu werden. Häufiger kommt die bedingte Anwendung von Stilen vor, die natürlich unterstützt wird. Ebenso lokale Konstanten und mathematische Ausdrücke, dank path.evaluate von Babel.
Atomare Ausgabe
Aufgrund der Funktionsweise von style9 kann jede Eigenschaftsdeklaration zu ihrer eigenen Klasse mit einer einzigen Eigenschaft werden. Wenn wir beispielsweise opacity: 0 an mehreren Stellen in unserem Code verwenden, existiert sie in der generierten CSS-Datei nur einmal. Der Vorteil hierbei ist, dass die CSS-Datei mit der Anzahl der *einzigartigen Deklarationen* wächst, nicht mit der Gesamtzahl der Deklarationen. Da die meisten Eigenschaften mehrmals verwendet werden, kann dies zu dramatisch kleineren CSS-Dateien führen. Beispielsweise benötigte die alte Facebook-Homepage 413 KB gzip-komprimiertes CSS. Das Redesign benötigt 74 KB für *alle* Seiten. Auch hier führt eine kleinere Dateigröße zu besserer Leistung.

Manche mögen sich darüber beschweren, dass die generierten Klassennamen nicht semantisch sind, dass sie opak sind und die Kaskade ignorieren. Das stimmt. Wir behandeln CSS als Kompilierungsziel. Aber aus gutem Grund. Indem wir bisher als selbstverständlich angesehene Best Practices in Frage stellen, können wir sowohl die Benutzer- als auch die Entwicklererfahrung verbessern.
Zusätzlich bietet style9 viele weitere großartige Funktionen, darunter: typisierte Stile mit TypeScript, Eliminierung ungenutzter Stile, die Möglichkeit, JavaScript-Variablen zu verwenden, und Unterstützung für Media Queries, Pseudo-Selektoren und Keyframes.
Hier ist die Anwendung
Zuerst installieren Sie es wie üblich
npm install style9
style9 verfügt über Plugins für Rollup, Webpack, Gatsby und Next.js, die alle auf einem Babel-Plugin basieren. Anleitungen zur Verwendung finden Sie im Repository. Hier verwenden wir das Webpack-Plugin.
const Style9Plugin = require('style9/webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
// This will transform the style9 calls
{
test: /\.(tsx|ts|js|mjs|jsx)$/,
use: Style9Plugin.loader
},
// This is part of the normal Webpack CSS extraction
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
// This will sort and remove duplicate declarations in the final CSS file
new Style9Plugin(),
// This is part of the normal Webpack CSS extraction
new MiniCssExtractPlugin()
]
};
Definition von Stilen
Die Syntax zum Erstellen von Stilen ähnelt stark anderen Bibliotheken. Wir beginnen mit dem Aufruf von style9.create mit Objekten von Stilen.
import style9 from 'style9';
const styles = style9.create({
button: {
padding: 0,
color: 'rebeccapurple'
},
padding: {
padding: 12
},
icon: {
width: 24,
height: 24
}
});
Da alle Deklarationen zu atomaren Klassen führen, funktionieren Kurzschreibweisen wie flex: 1 und background: blue nicht, da sie mehrere Eigenschaften setzen. Eigenschaften, die erweitert werden können, wie padding, margin, overflow usw., werden automatisch in ihre Langform-Varianten umgewandelt. Wenn Sie TypeScript verwenden, erhalten Sie einen Fehler bei der Verwendung nicht unterstützter Eigenschaften.
Auflösen von Stilen
Um einen Klassennamen zu generieren, können wir jetzt die von style9.create zurückgegebene Funktion aufrufen. Sie akzeptiert als Argumente die Schlüssel der Stile, die wir verwenden möchten.
const className = styles('button');
Die Funktion funktioniert so, dass Stile auf der rechten Seite Vorrang haben und mit den Stilen auf der linken Seite zusammengeführt werden, ähnlich wie bei Object.assign. Das Folgende würde zu einem Element mit einem Padding von 12px und einer rebeccapurple-Textfarbe führen.
const className = styles('button', 'padding');
Wir können Stile bedingt mit einem der folgenden Formate anwenden.
// logical AND
styles('button', hasPadding && 'padding');
// ternary
styles('button', isGreen ? 'green' : 'red');
// object of booleans
styles({
button: true,
green: isGreen,
padding: hasPadding
});
Diese Funktionsaufrufe werden während der Kompilierung entfernt und durch direkte String-Verkettung ersetzt. Die erste Zeile im obigen Code wird durch etwas wie 'c1r9f2e5 ' + hasPadding ? 'cu2kwdz ' : '' ersetzt. Es bleibt kein Laufzeit-Code übrig.
Kombination von Stilen
Wir können ein Style-Objekt erweitern, indem wir es mit einem Eigenschaftsnamen ansprechen und es an style9 übergeben.
const styles = style9.create({ blue: { color: 'blue; } });
const otherStyles = style9.create({ red: { color: 'red; } });
// will be red
const className = style9(styles.blue, otherStyles.red);
Ähnlich wie bei Funktionsaufrufen haben die Stile auf der rechten Seite Vorrang. In diesem Fall kann der Klassenname jedoch nicht statisch aufgelöst werden. Stattdessen werden die Eigenschaftswerte durch Klassen ersetzt und zur Laufzeit zusammengeführt. Die Eigenschaften werden wie zuvor zur CSS-Datei hinzugefügt.
Zusammenfassung
Die Vorteile von CSS-in-JS sind sehr real. Dennoch entstehen Leistungskosten, wenn wir Stile in unseren Code einbetten. Indem wir die Werte zur Build-Zeit extrahieren, können wir das Beste aus beiden Welten haben. Wir profitieren von der Zusammenlegung unserer Stile mit unserem Markup und der Möglichkeit, die vorhandene JavaScript-Infrastruktur zu nutzen, während wir gleichzeitig optimale Stylesheets generieren können.
Wenn style9 für Sie interessant klingt, werfen Sie einen Blick auf das Repository und probieren Sie es aus. Wenn Sie Fragen haben, öffnen Sie gerne ein Issue oder kontaktieren Sie uns.
Danksagungen
Dank an Giuseppe Gurgone für seine Arbeit an style-sheet und dss, Nicolas Gallagher für react-native-web, Satyajit Sahoo und alle bei Callstack für linaria, Christopher Chedeau, Sebastian McKenzie, Frank Yan, Ashley Watkins, Naman Goel und alle anderen, die an stylex bei Facebook gearbeitet haben, dafür, dass sie ihre Erkenntnisse öffentlich zugänglich gemacht haben. Und allen anderen, die ich vielleicht übersehen habe.
Links
- johanholmerin/style9
- Building the New facebook.com with React, GraphQL and Relay – 30. April 2019
- Building the New Facebook with React and Relay | Frank Yan – 30. Okt 2019
- Tech stack rebuild for a new Facebook.com – 8. Mai 2020
- johanholmerin/style9-components.macro: Styled Components API für style9 – experimentell
Ich denke, es sollte besser Style Components genannt werden.
Das ist nicht so beschreibend. Gute Namen sind selbsterklärend.
Wow! Das ist großartig! Ich kämpfe ein wenig mit Styled Components, ich finde einfach nicht, dass sie so einfach zu handhaben sind wie Klassen und Stylesheets. Das hier scheint das Beste aus beiden Welten zu sein!
Das ist wirklich ähnlich wie Treat.