Zu Beginn eines neuen Projekts erfolgt die Sass-Kompilierung im Handumdrehen. Das fühlt sich großartig an, besonders wenn es mit Browsersync gekoppelt ist, das die Stylesheets für uns im Browser neu lädt. Aber mit zunehmender Menge an Sass steigt die Kompilierungszeit. Das ist alles andere als ideal.
Es kann eine echte Plage sein, wenn die Kompilierungszeit über ein oder zwei Sekunden kriecht. Für mich ist das genug Zeit, um am Ende eines langen Tages den Fokus zu verlieren. Daher möchte ich eine Lösung vorstellen, die in einem WordPress CSS Editor namens Microthemer als Proof of Concept verfügbar ist.
Dies ist ein zweiteiliger Artikel. Der erste Teil richtet sich an Sass-Benutzer. Wir stellen die Grundprinzipien, Leistungsergebnisse und eine interaktive Demo vor. Der zweite Teil behandelt die technischen Details, wie Microthemer Sass schneller macht. Und erörtert, wie dies als npm-Paket implementiert werden kann, um schnelles, skalierbares Sass für eine viel breitere Entwicklergemeinschaft bereitzustellen.
Wie Microthemer Sass im Handumdrehen kompiliert
In gewisser Weise ist diese Leistungsoptimierung einfach. Microthemer kompiliert einfach weniger Sass-Code. Es greift nicht in den internen Kompilierungsprozess von Sass ein.
Um dem Sass-Compiler weniger Code zuzuführen, behält Microthemer ein Verständnis der Sass-Entitäten bei, die im gesamten Code-Basis verwendet werden, wie z. B. Variablen und Mixins. Wenn ein Selektor bearbeitet wird, kompiliert Microthemer nur diesen einzelnen Selektor plus alle zugehörigen Selektoren. Selektoren sind verwandt, wenn sie beispielsweise dieselben Variablen verwenden oder ein Selektor einen anderen erweitert. Mit diesem System bleibt die Sass-Kompilierung mit 3000 Selektoren genauso schnell wie mit einer Handvoll.
Leistungsergebnisse
Bei 3000 Selektoren beträgt die Kompilierungszeit etwa 0,05 Sekunden. Sie variiert natürlich. Manchmal liegt sie näher bei 0,1 Sekunden. Ein anderes Mal erfolgt die Kompilierung so schnell wie 0,01 Sekunden (10 ms).
Um sich selbst davon zu überzeugen, können Sie sich eine Videodemo ansehen. Oder Sie können mit dem Online-Microthemer-Spielplatz herumspielen (siehe Anweisungen unten).
Online Microthemer-Spielplatz
Der Online-Spielplatz macht es einfach, selbst mit Microthemer zu experimentieren.
Anleitung
- Gehen Sie zum Online Microthemer-Spielplatz.
- Aktivieren Sie die Unterstützung für Sass über Allgemein → Einstellungen → CSS / SCSS → SCSS aktivieren.
- Gehen Sie zu Ansicht → Vollständiger Code-Editor → Ein, um globale Variablen, Mixins und Funktionen hinzuzufügen.
- Wechseln Sie zurück zur Hauptansicht der Benutzeroberfläche (Ansicht → Vollständiger Code-Editor → Aus).
- Erstellen Sie Selektoren über die Schaltfläche "Target".
- Fügen Sie Sass-Code über den Editor links von der Schriftartengruppe hinzu.
- Nach jeder Änderung können Sie sehen, welcher Code von Microthemer im Kompilierungsprozess enthalten war, indem Sie auf Ansicht → Generiertes CSS → Vorherige SCSS-Kompilierung klicken.
- Um zu sehen, wie dies in großem Maßstab funktioniert, können Sie Vanilla-CSS aus einem großen Stylesheet in Microthemer importieren über Pakete → Importieren → CSS-Stylesheet (der Import von Sass wird (noch) nicht unterstützt).
Wünschen Sie dies als npm-Paket?
Die selektive Kompilierungstechnik von Microthemer könnte auch als npm-Paket geliefert werden. Aber die Frage ist, sehen Sie dafür einen Bedarf? Könnte Ihre lokale Sass-Umgebung eine Geschwindigkeitssteigerung gebrauchen? Wenn ja, hinterlassen Sie bitte einen Kommentar unten.
Der Rest dieses Artikels richtet sich an diejenigen, die Werkzeuge für die Community entwickeln. Sowie an diejenigen, die neugierig sind, wie diese Herausforderung angegangen wurde.
Der Microthemer-Weg zur Sass-Kompilierung
Wir werden uns gleich mit einigen Codebeispielen beschäftigen. Betrachten wir jedoch zuerst die Hauptziele der Anwendung.
1. Kompilieren minimalen Codes
Wir möchten den einen bearbeiteten Selektor kompilieren, wenn er keine Beziehung zu anderen Selektoren hat, oder mehrere Selektoren mit verwandten Sass-Entitäten – aber nicht mehr als nötig.
2. Reaktiv auf Codeänderungen
Wir wollen jede gefühlte Wartezeit für die Sass-Kompilierung eliminieren. Wir wollen auch nicht zu viele Daten zwischen Benutzereingaben verarbeiten.
3. Gleiche CSS-Ausgabe
Wir möchten das gleiche CSS zurückgeben, das eine vollständige Kompilierung erzeugen würde, jedoch für einen Teilcode.
Sass-Beispiele
Der folgende Code dient als Referenzpunkt für diesen Artikel. Er deckt alle Szenarien ab, die unser selektiver Compiler handhaben muss. Wie globale Variablen, Mixin-Nebeneffekte und erweiterte Selektoren.
Variablen, Funktionen und Mixins
$primary-color: green;
$secondary-color: red;
$dark-color: black;
@function toRem($px, $rootSize: 16){
@return #{$px / $rootSize}rem;
}
@mixin rounded(){
border-radius: 999px;
$secondary-color: blue !global;
}
Selektoren
.entry-title {
color: $dark-color;
}
.btn {
display: inline-block;
padding: 1em;
color: white;
text-decoration: none;
}
.btn-success {
@extend .btn;
background-color: $primary-color;
@include rounded;
}
.btn-error {
@extend .btn;
background-color: $secondary-color;
}
// Larger screens
@media (min-width: 960px) {
.btn-success {
border:4px solid darken($primary-color, 10%);
&::before {
content: "\2713"; // unicode tick
margin-right: .5em;
}
}
}
Die Microthemer-Oberfläche
Microthemer hat zwei Hauptbearbeitungsansichten.
Ansicht 1: Vollständiger Code
Wir bearbeiten den vollständigen Code-Editor auf die gleiche Weise wie eine reguläre Sass-Datei. Dort kommen globale Variablen, Funktionen, Mixins und Imports hinein.

Ansicht 2: Visuell
Die visuelle Ansicht hat eine einfache Selektorarchitektur. Jeder CSS-Selektor ist ein separater UI-Selektor. Diese UI-Selektoren sind in Ordnern organisiert.

Da Microthemer einzelne Selektoren segmentiert, erfolgt die Analyse auf sehr granularer Ebene – ein Selektor nach dem anderen.

Hier ist eine kleine Quizfrage für Sie. Die Variable $secondary-color ist am oberen Rand der Ansicht "Vollständiger Code" auf red gesetzt. Warum ist der Fehlerknopf in den vorherigen Screenshots blau? Tipp: Es hat etwas mit Nebeneffekten von Mixins zu tun. Dazu gleich mehr.
Drittanbieter-Bibliotheken
Ein riesiges Dankeschön an die Autoren der folgenden JavaScript-Bibliotheken, die Microthemer verwendet
- Gonzales PE – Konvertiert Sass-Code in ein Abstract Syntax Tree (AST) JavaScript-Objekt.
- Sass.js – Konvertiert Sass in CSS-Code im Browser. Es verwendet Web Worker, um die Kompilierung in einem separaten Thread auszuführen.
Datenobjekte
Nun zu den kniffligen Details. Die Ermittlung einer geeigneten Datenstruktur erforderte einige Versuche und Irrtümer. Aber sobald sie richtig war, ergab sich die Anwendungslogik ganz natürlich. Wir beginnen also mit der Erklärung der wichtigsten Datenspeicher und schließen mit einer kurzen Zusammenfassung der Verarbeitungsschritte ab.
Microthemer verwendet vier Haupt-JavaScript-Objekte zur Speicherung von Anwendungsdaten.
projectCode: Speichert den gesamten Projektcode, unterteilt in einzelne Elemente für einzelne Selektoren.projectEntities: Speichert alle Variablen, Funktionen, Mixins, extends und Imports, die im Projekt verwendet werden, sowie die Speicherorte, an denen diese Entitäten verwendet werden.connectedEntities: Speichert die Verbindungen, die ein Codeteil mit Sass-Entitäten des Projekts hat.compileResources: Speichert die selektiven Kompilierungsdaten nach einer Änderung der Code-Basis.
projectCode
Das Objekt projectCode ermöglicht es uns, Sass-Code-Teile schnell abzurufen. Diese Teile kombinieren wir dann zu einer einzigen Zeichenkette für die Kompilierung.
files: Mit Microthemer speichert dies den Code, der zur oben beschriebenen Ansicht "Vollständiger Code" hinzugefügt wird. Bei einer npm-Implementierung würde sichfilesauf tatsächliche .sass- oder .scss-Systemdateien beziehen.folders: UI-Ordner von Microthemer, die segmentierte UI-Selektoren enthalten.index: Die Reihenfolge eines Ordners oder eines Selektors innerhalb eines Ordners.itemData: Der eigentliche Code für das Element, weiter erläutert im nächsten Code-Snippet.
var projectCode = {
// Microthemer full code editor
files: {
full_code: {
index: 0,
itemData: itemData
}
},
// Microthemer UI folders and selectors
folders: {
content_header: {
index:100,
selectors: {
'.entry-title': {
index:0,
itemData: itemData
},
}
},
buttons: {
index:200,
selectors: {
'.btn': {
index:0,
itemData: itemData
},
'.btn-success': {
index:1,
itemData: itemData
},
'.btn-error': {
index:2,
itemData: itemData
}
}
}
}
};
itemData für .btn-success Selektor
Das folgende Codebeispiel zeigt die itemData für den Selektor .btn-success.
sassCode: Wird verwendet, um die Kompilierungszeichenkette zu erstellen.compiledCSS: Speichert kompiliertes CSS zum Schreiben in ein Stylesheet oder einen Style-Knoten im Dokumentkopf.sassEntities: Sass-Entitäten für einen einzelnen Selektor oder eine Datei. Ermöglicht die Analyse vor und nach der Änderung und wird zum Aufbau desprojectEntities-Objekts verwendet.mediaQueries: Gleiche Daten wie oben, jedoch für einen Selektor, der innerhalb einer Media Query verwendet wird.
var itemData = {
sassCode: ".btn-success { @extend .btn; background-color: $primary-color; @include rounded; }",
compiledCSS: ".btn-success { background-color: green; border-radius: 999px; }",
sassEntities: {
extend: {
'.btn': {
values: ['.btn']
}
},
variable: {
primary_color: {
values: [1]
}
},
mixin: {
rounded: {
values: [1]
}
}
},
mediaQueries: {
'min-width(960px)': {
sassCode: ".btn-success { border:4px solid darken($primary-color, 10%); &::before { content: '\\2713'; margin-right: .5em; } }",
compiledCSS: ".btn-success::before { content: '\\2713'; margin-right: .5em; }",
sassEntities: {
variable: {
primary_color: {
values: [1]
}
},
function: {
darken: {
values: [1]
}
}
}
}
}
};
projectEntities
Das Objekt projectEntities ermöglicht es uns zu prüfen, welche Selektoren bestimmte Sass-Entitäten verwenden.
variable,function,mixin,extend: Der Typ der Sass-Entität.- Z.B.
primary_color: Der Name der Sass-Entität. Microthemer normalisiert Bindestrich-Namen, da Sass Bindestriche und Unterstriche austauschbar verwendet. values: Ein Array von Deklarationswerten oder Instanzen. Instanzen werden durch die Zahl 1 repräsentiert. Der Gonzales PE Sass-Parser konvertiert numerische Deklarationswerte in Zeichenketten. Daher habe ich die Ganzzahl 1 zur Kennzeichnung von Instanzen gewählt.itemDeps: Ein Array von Selektoren, die die Sass-Entität verwenden. Dies wird im nächsten Code-Snippet weiter erläutert.relatedEntities: Unser Mixinroundedhat den Nebeneffekt, dass die globale Variable$secondary-coloraufblueaktualisiert wird, daher der blaue Fehlerknopf. Dieser Nebeneffekt macht die Entitätenroundedund$secondary-colorvoneinander abhängig. Wenn also die Variable$secondary-colorenthalten ist, sollte auch der Mixinroundedenthalten sein und umgekehrt.
var projectEntities = {
variable: {
primary_color: {
values: ['green', 1],
itemDeps: itemDeps
},
secondary_color: {
values: ["red", "blue !global", 1],
itemDeps: itemDeps,
relatedEntities: {
mixin: {
rounded: {}
}
}
},
dark_color: {
values: ["black", 1],
itemDeps: itemDeps
}
},
function: {
darken: {
values: [1]
},
toRem: {
values: ["@function toRem($px, $rootSize: 16){↵ @return #{$px / $rootSize}rem;↵}", 1],
itemDeps: itemDeps
}
},
mixin: {
rounded: {
values: ["@mixin rounded(){↵ border-radius:999px;↵ $secondary-color: blue !global;↵}", 1],
itemDeps: itemDeps,
relatedEntities: {
variable: {
secondary_color: {
values: ["blue !global"],
}
}
}
}
},
extend: {
'.btn': {
values: ['.btn', '.btn'],
itemDeps: itemDeps
}
}
};
itemDeps für die Sass-Entität $primary-color
Das folgende Codebeispiel zeigt die itemDeps für die Variable $primary-color (primary_color). Die Variable $primary-color wird von zwei Formen des Selektors .btn-success verwendet, einschließlich eines Selektors innerhalb der Media Query min-width(960px).
path: Wird verwendet, um Selektordaten aus demprojectCode-Objekt abzurufen.mediaQuery: Wird beim Aktualisieren von Style-Knoten oder beim Schreiben in ein CSS-Stylesheet verwendet.
var itemDeps = [
{
path: ["folders", 'header', 'selectors', '.btn-success'],
},
{
path: ["folders", 'header', 'selectors', '.btn-success', 'mediaQueries', 'min-width(960px)'],
mediaQuery: 'min-width(960px)'
}
];
connectedEntities
Das Objekt connectedEntities ermöglicht es uns, verwandte Codeteile zu finden. Wir füllen es nach einer Änderung der Code-Basis. Wenn wir also die Deklaration font-size aus dem Selektor .btn entfernen würden, würde sich der Code von diesem ändern
.btn {
display: inline-block;
padding: 1em;
color: white;
text-decoration: none;
font-size: toRem(21);
}
...zu diesem:
.btn {
display: inline-block;
padding: 1em;
color: white;
text-decoration: none;
}
Und wir würden die Analyse von Microthemer im folgenden connectedEntities-Objekt speichern.
-
changed: Die Analyse der Änderung, die die Entfernung der FunktiontoRemerfasst.actions: Ein Array von Benutzeraktionen.form: Deklaration (z. B.$var: 18px) oder Instanz (z. B.font-size: $var).value: Ein Textwert für eine Deklaration oder die Ganzzahl 1 für eine Instanz.
coDependent: Erweiterte Selektoren müssen immer zusammen mit dem erweiternden Selektor kompiliert werden, und umgekehrt. Die Beziehung ist co-dependent. Variablen, Funktionen und Mixins sind nur semi-dependent. Instanzen müssen mit Deklarationen kompiliert werden, aber Deklarationen müssen nicht mit Instanzen kompiliert werden. Microthemer behandelt sie jedoch der Einfachheit halber als co-dependent. In Zukunft wird eine Logik hinzugefügt, um unnötige Instanzen zu filtern, aber dies wurde für die erste Veröffentlichung weggelassen.related: Der Mixinroundedsteht in Beziehung zur Variable$secondary-color. Er aktualisiert diese Variable mit dem Flagglobal. Die beiden Entitäten sind co-dependent; sie sollten immer zusammen kompiliert werden. Aber in unserem Beispiel verwendet der Selektor.btnden Mixinroundednicht. Daher ist die Eigenschaftrelatedunten nicht mit etwas gefüllt.
var connectedEntities = {
changed: {
function: {
toRem: {
actions: [{
action: "removed",
form: "instance",
value: 1
}]
}
}
},
coDependent: {
extend: {
'.btn': {}
}
},
related: {}
};
compileResources
Das Objekt compileResources ermöglicht es uns, einen Teilcode in der richtigen Reihenfolge zu kompilieren. Im vorherigen Abschnitt haben wir die Deklaration der Schriftgröße entfernt. Der folgende Code zeigt, wie das Objekt compileResources nach dieser Änderung aussehen würde.
-
compileParts: Ein Array von Ressourcen, die kompiliert werden sollen.path: Wird verwendet, um die EigenschaftcompiledCSSdes relevantenprojectCode-Elements zu aktualisieren.sassCode: Wird zum Aufbau dersassStringfür die Kompilierung verwendet. Wir fügen jedem Teil einen CSS-Kommentar hinzu (/*MTPART*/). Dieser Kommentar wird verwendet, um die kombinierte CSS-Ausgabe in das ArraycssPartsaufzuteilen.
sassString: Eine Zeichenkette von Sass-Code, die zu CSS kompiliert wird.cssParts: CSS-Ausgabe in Form eines Arrays. Die Array-Schlüssel fürcssPartsstimmen mit dem ArraycompilePartsüberein.
var compileResources = {
compileParts: [
{
path: ["files", "full_code"],
sassCode: "/*MTFILE*/$primary-color: green; $secondary-color: red; $dark-color: black; @function toRem($px, $rootSize: 16){ @return #{$px / $rootSize}rem; } @mixin rounded(){ border-radius:999px; $secondary-color: blue !global;}/*MTPART*/"
},
{
path: ["folders", "buttons", ".btn"],
sassCode: ".btn { display: inline-block; padding: 1em; color: white; text-decoration: none; }/*MTPART*/"
},
{
path: ["folders", "buttons", ".btn-success"],
sassCode: ".btn-success { @extend .btn; background-color: $primary-color; @include rounded; }/*MTPART*/"
},
{
path: ["folders", "buttons", ".btn-error"],
sassCode: ".btn-error { @extend .btn; background-color: $secondary-color; }/*MTPART*/"
}
],
sassString:
"/*MTFILE*/$primary-color: green; $secondary-color: red; $dark-color: black; @function toRem($px, $rootSize: 16){ @return #{$px / $rootSize}rem; } @mixin rounded(){ border-radius:999px; $secondary-color: blue !global;}/*MTPART*/"+
".btn { display: inline-block; padding: 1em; color: white; text-decoration: none;}/*MTPART*/"+
".btn-success {@extend .btn; background-color: $primary-color; @include rounded;}/*MTPART*/"+
".btn-error {@extend .btn; background-color: $secondary-color;}/*MTPART*/",
cssParts: [
"/*MTFILE*//*MTPART*/",
".btn, .btn-success, .btn-error { display: inline-block; padding: 1em; color: white; text-decoration: none;}/*MTPART*/",
".btn-success { background-color: green; border-radius: 999px;}/*MTPART*/",
".btn-error { background-color: blue;}/*MTPART*/"
]
};
Warum wurden vier Ressourcen aufgenommen?
full_code: Die Sass-EntitättoRemwurde geändert und derfull_code-Ressource enthält die Deklaration der FunktiontoRem..btn: Der Selektor wurde bearbeitet..btn-success: Verwendet@extend .btnund muss daher immer mit.btnkompiliert werden. Der kombinierte Selektor wird zu.btn, .btn-success..btn-error: Dies verwendet ebenfalls@extend .btnund muss daher aus den gleichen Gründen wie.btn-successaufgenommen werden.
Zwei Selektoren werden nicht aufgenommen, da sie nicht mit dem Selektor .btn verwandt sind.
.entry-title.btn-success(innerhalb der Media Query)
Rekursive Ressourcensammlung
Abgesehen von der Datenstruktur war die zeitaufwendigste Herausforderung herauszufinden, wie der richtige Teil von Sass-Code bezogen werden kann. Wenn ein Codeteil mit einem anderen verbunden ist, müssen wir auch für den zweiten Teil Verbindungen prüfen. Es gibt eine Kettenreaktion. Um dies zu unterstützen, ist die folgende Funktion gatherCompileResources *rekursiv*.
- Wir durchlaufen das Objekt
connectedEntitiesbis auf die Ebene der Sass-Entitätsnamen. - Wir verwenden Rekursion, wenn eine Funktion oder ein Mixin Nebeneffekte hat (z. B. globale Variablen aktualisieren).
- Die Funktion
checkObjectgibt den Wert eines Objekts auf einer bestimmten Ebene zurück oder false, wenn kein Wert vorhanden ist. - Die Funktion
updateObjectsetzt den Wert eines Objekts auf einer bestimmten Ebene. - Wir fügen abhängige Ressourcen zum Array
compilePartshinzu und verwendenabsoluteIndexals Schlüssel. - Microthemer berechnet
absoluteIndex, indem es den Ordnerindex zum Selektorindex addiert. Dies funktioniert, da Ordnerindizes in Hundertern inkrementieren und die maximale Anzahl von Selektoren pro Ordner 40 beträgt, was weniger als hundert ist. - Wir verwenden Rekursion, wenn Ressourcen, die dem Array
compilePartshinzugefügt werden, ebenfalls co-abhängige Beziehungen haben.
function gatherCompileResources(compileResources, connectedEntities, projectEntities, projectCode, config){
let compileParts = compileResources.compileParts;
// reasons: changed / coDependent / related
const reasons = Object.keys(connectedEntities);
for (const reason of reasons) {
// types: variable / function / mixin / extend
const types = Object.keys(connectedEntities[reason]);
for (const type of types) {
// names: e.g. toRem / .btn / primary_color
const names = Object.keys(connectedEntities[reason][type]);
for (const name of names) {
// check side-effects for Sass entity (if not checked already)
if (!checkObject(config.relatedChecked, [type, name])){
updateObject(config.relatedChecked, [type, name], 1);
const relatedEntities = checkObject(projectEntities, [type, name, 'relatedEntities']);
if (relatedEntities){
compileParts = gatherCompileResources(
compileResources, { related: relatedEntities }, projectEntities, projectCode, config
);
}
}
// check if there are dependent pieces of code
const itemDeps = checkObject(projectEntities, [type, name, 'itemDeps']);
if (itemDeps && itemDeps.length > 0){
for (const dep of itemDeps) {
let path = dep.path,
resourceID = path.join('.');
if (!config.resourceAdded[resourceID]){
// if we have a valid resource
let resource = checkObject(projectCode, path);
if (resource){
config.resourceAdded[resourceID] = 1;
// get folder index + resource index
let absoluteIndex = getAbsoluteIndex(path);
// add compile part
compileParts[absoluteIndex] = {
sassCode: resource.sassCode,
mediaQuery: resource.mediaQuery,
path: path
};
// if resource is co-dependent, pull in others
let coDependent = getCoDependent(resource);
if (coDependent){
compileParts = gatherCompileResources(
compileResources, { coDependent: coDependent }, projectEntities, projectCode, config
);
}
}
}
}
}
}
}
}
return compileParts;
}
Der Anwendungsprozess
Wir haben uns nun mit den technischen Aspekten befasst. Um zu sehen, wie alles zusammenhängt, gehen wir die Datenverarbeitungsschritte durch.
Von der Tastatureingabe zum Rendern des Stils
- Eine Benutzereingabe löst das Ereignis "Textarea-Änderung" aus.
- Wir wandeln den einzelnen bearbeiteten Selektor in ein
sassEntities-Objekt um. Dies ermöglicht den Vergleich mit den Sass-Entitäten vor der Bearbeitung:projectCode > dataItem > sassEntities. - Wenn sich Sass-Entitäten geändert haben
- Wir aktualisieren
projectCode > dataItem > sassEntities. - Wenn sich eine Regel
@extendgeändert hat- Wir durchsuchen das Objekt
projectCode, um passende Selektoren zu finden. - Wir speichern den
pathfür passende Selektoren auf dem aktuellen Datenelement:projectCode > dataItem > sassEntities > extend > target > [ path ].
- Wir durchsuchen das Objekt
- Wir bauen das Objekt
projectEntitiesneu auf, indem wir das ObjektprojectCodedurchlaufen. - Wir füllen
connectedEntities > changedmit der Analyse der Änderung. - Wenn Entitäten
extend,variable,functionodermixinvorhanden sind- Wir füllen
connectedEntities > coDependentmit den relevanten Entitäten.
- Wir füllen
- Wir aktualisieren
- Die rekursive Funktion
gatherCompileResourcesverwendet das ObjektconnectedEntities, um das ObjektcompileResourceszu füllen. - Wir verketten das Array
compileResources > compilePartszu einer einzigen Sass-Zeichenkette. - Wir kompilieren die einzelne Sass-Zeichenkette zu CSS.
- Wir verwenden Kommentar-Trennzeichen, um die Ausgabe in ein Array zu teilen:
compileResources > cssParts. Dieses Array stimmt mit dem ArraycompileResources > compilePartsüber übereinstimmende Array-Schlüssel überein. - Wir verwenden Ressourcenpfade, um das Objekt
projectCodemit kompiliertem CSS zu aktualisieren. - Wir schreiben das CSS für einen bestimmten Ordner oder eine Datei in einen Style-Knoten im Dokumentkopf für die sofortige Stilwiedergabe. Auf der Serverseite schreiben wir alles CSS in ein einziges Stylesheet.
Überlegungen für npm
Für ein npm-Paket gibt es einige zusätzliche Überlegungen. In einer typischen NodeJS-Entwicklungsumgebung
- Benutzer bearbeiten Selektoren als Teil einer größeren Datei, anstatt einzeln.
- Sass-Imports werden wahrscheinlich eine größere Rolle spielen.
Code segmentierung
Die visuelle Ansicht von Microthemer segmentiert einzelne Selektoren. Dies macht das Parsen von Code in ein sassEntities-Objekt super schnell. Das Parsen ganzer Dateien kann eine andere Geschichte sein, besonders bei großen.
Vielleicht gibt es Techniken zur virtuellen Segmentierung von Systemdateien? Aber nehmen wir an, es gibt keinen Weg am Parsen ganzer Dateien vorbei. Oder es erscheint einfach sinnvoll für eine erste Veröffentlichung. Vielleicht reicht es aus, Endbenutzer anzuweisen, Sass-Dateien klein zu halten, um die besten Ergebnisse zu erzielen.
Sass-Importe
Zum Zeitpunkt der Erstellung analysiert Microthemer keine Importdateien. Stattdessen werden alle Sass-Importe einbezogen, sobald ein Selektor Sass-Entitäten verwendet. Dies ist eine vorläufige Lösung für die erste Veröffentlichung, die im Kontext von Microthemer in Ordnung ist. Aber ich glaube, eine npm-Implementierung sollte die Sass-Nutzung über alle Projektdateien hinweg verfolgen.
Unser projectCode-Objekt hat bereits eine files-Eigenschaft zum Speichern von Dateidaten. Ich schlage vor, Dateiverzeichnisse relativ zur Haupt-Sass-Datei zu berechnen. Zum Beispiel hätte die Datei, die in der ersten @import-Regel verwendet wird, index: 0, die nächste index: 1 und so weiter. Wir müssten @import-Regeln innerhalb von importierten Dateien scannen, um diese Indizes korrekt zu berechnen.
Wir müssten auch absoluteIndex anders berechnen. Im Gegensatz zu Microthemer-Ordnern können Systemdateien beliebig viele Selektoren haben. Das compileParts-Array muss möglicherweise ein Objekt sein, das ein Array von Teilen für jede Datei speichert. Auf diese Weise verfolgen wir nur die Selektorindizes innerhalb einer bestimmten Datei und verketten dann die compileParts-Arrays in der Reihenfolge der Dateien.
Fazit
Dieser Artikel stellt eine neue Methode zur selektiven Kompilierung von Sass-Code vor. Eine nahezu sofortige Sass-Kompilierung war für Microthemer notwendig, da es sich um einen Live-CSS-Editor handelt. Und das Wort "live" bringt bestimmte Geschwindigkeitserwartungen mit sich. Aber es kann auch für andere Umgebungen wünschenswert sein, wie z. B. Node.js. Dies liegt an den Node.js- und Sass-Benutzern zu entscheiden und hoffentlich ihre Gedanken unten in den Kommentaren zu teilen. Wenn die Nachfrage besteht, hoffe ich, dass ein npm-Entwickler die von mir geteilten Punkte nutzen kann.
Zögern Sie bitte nicht, Fragen dazu in meinem Forum zu stellen. Ich helfe Ihnen gerne weiter.
Hallo, nur am Rande https://webinista.com/updates/dont-use-extend-sass/
Hallo Szymon,
Danke für deinen Kommentar. Ja, ich habe viele Artikel gelesen, die von der Verwendung von @extend abraten, als ich diese Optimierung plante. Und ich war von den Argumenten überzeugt. Aber solange Sass die Regel nicht abschafft, muss die selektive Kompilierung sie berücksichtigen. Was schade ist, denn die Berücksichtigung von @extend war einer der fummeligsten Aspekte der Anwendung!
Der webinista-Artikel erwähnt zu Recht, dass die Verwendung von @extend je nach Implementierung von "schlechte Idee" zu "nahezu genial" reicht: Da es sich um ein kniffliges Thema handelt, wird es oft missbraucht. Das scheint hier nicht der Fall zu sein. Nachdem ich Sebastians Lösung getestet hatte, fand ich keine Probleme.
Danke, dass Sie auf die Nuance im webinista-Artikel zu @extend Noah Quinn aufmerksam gemacht haben. Ich bin froh zu hören, dass Sie meine Lösung ohne Probleme nutzen konnten.
Hallo Sebastian, das ist ein interessanter Ansatz. Wie würde er globale Mixins behandeln, die außerhalb von Selektoren ausgelöst werden und deren Funktion rein darin besteht, Nebeneffekte zu erzeugen? Ein Beispiel dafür sind "Setup"-Mixins, die Variablen parsen, erstellen oder transformieren, von denen später Code abhängt...
Hallo Lu,
Danke für deinen Kommentar und sorry für die langsame Antwort. Mein Sohn wurde am Dienstag geboren und wir sind noch im Krankenhaus zur Überwachung. Ich sollte nächste Woche richtig antworten können.
Vielen Dank,
Sebastian
Hallo Lu nochmals,
Ich denke, hier gibt es vielleicht zwei Fragen.
F1. Wie behandelt Microthemer Sass-Code außerhalb von Selektoren?
A1. Die vollständige Codeansicht ermöglicht beliebigen Sass-Code wie Mixin-, Funktions- und Variablendeklarationen. Code, der dort eingegeben wird, geht dem Code vor, der zu den einzelnen UI-Selektoren hinzugefügt wird. Es ist derzeit nicht möglich, beliebige Sass-Schnipsel zwischen die UI-Selektoren einzufügen, obwohl dies in Zukunft unterstützt wird.
Für eine npm-Implementierung würden die UI-Ordner von Microthemer durch reguläre Systemdateien ersetzt werden (siehe Abschnitt "Überlegungen für npm"). Und die Code-Segmentierung würde auf Dateiebene erfolgen, anstatt auf der Ebene einzelner Selektoren (es sei denn, jemand kennt einen Weg, Systemdateien virtuell zu segmentieren). Bei regulären Dateien können beliebige Sass-Schnipsel frei zwischen Selektoren eingefügt werden.
F2. Wie behandelt Microthemer Nebeneffekte in Mixins?
A2. Microthemer unterstützt Nebeneffekte wie das Setzen globaler Variablen sowohl für Mixins als auch für Funktionen. Ich habe gehört, dass Nebeneffekte für Funktionen eher abgeraten werden, aber MT unterstützt sie, weil Sass sie unterstützt.
Was die Funktionsweise betrifft: Wenn Microthemer Sass-Code parst, protokolliert es alle Nebeneffekte. Beachten Sie, dass der Mixin `rounded()` im vorherigen Artikel den Nebeneffekt hat, die Variable `$secondary-color` auf blau zu setzen. Sehen Sie sich dann das `projectEntities`-Objekt an – der Schlüssel `relatedEntities` ist sowohl für die Eigenschaften `secondary_color` als auch für `rounded` gesetzt. Dies wird dann in der Funktion `gatherCompileResources()` überprüft, um jeglichen Code abzurufen, der entweder von der Funktion `rounded()` oder von der Variablen `$secondary-color` abhängt. Im Wesentlichen behandelt Microthemer diese beiden Sass-Entitäten als voneinander abhängig. Und kompiliert daher immer die ursprünglichen Deklarationen sowie jeglichen Code, der sie zusammen aktualisiert oder verwendet.
In Ihrem Setup-Mixin-Beispiel wird also, wenn der bearbeitete Selektor die Variablen des Setup-Mixins verwendet, das Setup-Mixin in die selektive Kompilierung einbezogen. Die Indizierung wird verwendet, um sicherzustellen, dass teilweiser Sass-Code immer in der richtigen Reihenfolge kompiliert wird.
Beantwortet das Ihre Frage?
Bitte lassen Sie mich wissen, wenn Sie etwas genauer erläutern möchten.
Danke!
Sebastian
Gibt es Neuigkeiten zu diesem npm-Paket?
Hey, danke für deinen Kommentar. Ich habe bisher kein npm-Paket gehört, das Sass mit dieser Optimierung kompiliert...