Wann und wie werden mehrere Versionen von Sass unterstützt?

Neulich überprüfte ich den Sass-Code des Jeet-Rastersystems, nur um der Sache willen. Nach einigen Kommentaren zum GitHub-Repository verstand ich, dass die Betreuer von Jeet noch nicht bereit waren, zu Sass 3.3 zu wechseln. In der Tat ist es genauer gesagt, Jeet zu sagen Benutzer Sie sind nicht bereit, zu Sass 3.3 überzugehen, je nach der Anzahl der Probleme, mit denen Jeet begonnen hat, die Sass 3.3-Funktionen zu verwenden. 

Der Punkt ist aber, dass Jeet nicht alle coolen und glänzenden Sachen von Sass 3.3 bekommen kann. Oder kann es?

* existiert Funktionen

Wenn Sie wissen, welche Version 3.3 für Sass verfügbar ist, wissen Sie möglicherweise, dass dem Kern ein paar Hilfsfunktionen hinzugefügt wurden, die dazu beitragen sollen, dass Framework-Entwickler mehrere Versionen von Sass gleichzeitig unterstützen:

  • globale Variable existiert ($ name): prüft, ob eine Variable im globalen Bereich vorhanden ist
  • Variable existiert ($ name): prüft, ob im aktuellen Gültigkeitsbereich eine Variable vorhanden ist
  • Funktion existiert ($ name): prüft, ob eine Funktion im globalen Bereich vorhanden ist
  • mixin-exists ($ name): prüft, ob im globalen Bereich ein Mixin vorhanden ist

Da ist auch ein feature-exists ($ name) Funktion, aber ich bin wirklich nicht sicher, was es tut, da die Docs ziemlich ausweichen. Ich habe sogar einen Blick auf den Code der Funktion geworfenbool (Sass.has_feature? (feature.value)), was nicht viel hilft.

Wie auch immer, wir haben ein paar Funktionen, mit denen Sie überprüfen können, ob eine Funktion, ein Mixin oder eine Variable existiert. Das ist sehr schön. Zeit, um weiterzuziehen.

Erkennung der Sass-Version

Okay, neue Funktionen, ziemlich cool. Was passiert aber, wenn wir eine dieser Funktionen in einer Sass 3.2.x-Umgebung verwenden? Lass es uns an einem kleinen Beispiel herausfinden.

// Eine Variable definieren $ my-awesome-variable: 42; // irgendwo sonst im Code $ does-my-awesome-variable-exist: variable-exists ('my-awesome-variable'); // Sass 3.3 -> 'true' // Sass 3.2 -> 'variable-exists (' my-awesome-variable ')' 

Wie Sie an den Ergebnissen sehen können, stürzt Sass 3.2 nicht ab oder wirft keine Fehler aus. Es parst Variable existiert ('my-awesome-variable') als Schnur, also im Grunde "variable-exists ('my-awesome-variable')". Um zu prüfen, ob es sich um einen booleschen Wert oder eine Zeichenfolge handelt, können Sie einen sehr einfachen Test schreiben:

$ return-type: type-of ($ does-my-awesome-variable-exist); // Sass 3.3 -> 'bool' // Sass 3.2 -> 'string' 

Wir können nun die Sass-Version aus dem Code heraus erkennen. Wie großartig ist das? Eigentlich erkennen wir die Sass-Version nicht genau. Wir finden eher einen Weg, um zu definieren, ob wir Sass 3.2 oder Sass 3.3 verwenden, aber das ist alles, was wir in diesem Fall brauchen.

Progressive Enhancement

Schauen wir uns an, um die Sass-Funktionen progressiv zu verbessern. Zum Beispiel könnten wir native Tools verwenden, wenn sie verfügbar sind (Sass 3.3), oder auf benutzerdefinierte Tools zurückgreifen, wenn dies nicht der Fall ist (Sass 3.2). Das habe ich Jeet bezüglich des vorgeschlagen ersetzen-nth () Funktion, die verwendet wird, um einen Wert an einem bestimmten Index zu ersetzen.

So geht's uns könnte TU es:

@ function replace-nth ($ list, $ index, $ value) // Wenn 'set-nth' existiert (Sass 3.3) @if function-exists ('set-nth') == true @return set- nth ($ list, $ index, $ value);  // Else es ist Sass 3.2 $ result: (); $ index: if ($ index < 0, length($list) + $index + 1, $index); @for $i from 1 through length($list)  $result: append($result, if($i == $index, $value, nth($list, $i)));  @return $result;  

Und dann nehme ich an, du bist wie ...  Was ist der Sinn, dies zu tun, wenn wir es trotzdem für Sass 3.2 funktionieren lassen können?? Faire Frage. ich würde sagen Performance. In unserem Fall, Set-nth ist eine native Funktion von Sass 3.3, was bedeutet, dass sie in Ruby funktioniert, was bedeutet, dass sie wesentlich schneller ist als eine benutzerdefinierte Sass-Funktion. Grundsätzlich werden Änderungen auf der Ruby-Seite und nicht im Sass-Compiler vorgenommen.

Ein anderes Beispiel (noch von Jeet) wäre a umkehren Funktion, um eine Liste von Werten umzukehren. Als ich SassyLists zum ersten Mal veröffentlichte, gab es kein Sass 3.3. Das Umkehren einer Liste würde bedeuten, eine neue Liste zu erstellen, die ursprüngliche Liste zu durchlaufen und Werte an die neue anzuhängen. Es hat die Arbeit gut gemacht. Nun, da wir Zugang zum haben Set-nth Funktion von Sass 3.3 gibt es eine viel bessere Möglichkeit, eine Liste umzukehren: das Austauschen von Indizes.

Um die Leistung zwischen beiden Implementierungen zu vergleichen, habe ich 500 Mal versucht, das lateinische Alphabet (eine Liste von 26 Elementen) umzukehren. Die Ergebnisse waren mehr oder weniger:

  • zwischen 2s und 3s mit dem "3.2 - Ansatz" (using anhängen)
  • nie über 2s mit dem "3.3 - Ansatz" (mit Set-nth)

Der Unterschied wäre bei einer längeren Liste sogar noch größer, einfach weil das Austauschen von Indizes viel schneller ist als das Anhängen von Werten. Also versuchte ich noch einmal, ob wir beide Welten optimal nutzen können. Folgendes habe ich mir ausgedacht:

@ function reverse ($ list) // Wenn 'set-nth' existiert (Sass 3.3) @if function-exists ('set-nth') == true @für $ i von 1 bis zur Etage (Länge ($ list) / 2) $ list: set-nth (set-nth ($ list, $ i, nth ($ list, - $ i)), - $ i, nth ($ list, $ i));  @return $ list;  // Else es ist Sass 3.2 $ result: (); @für $ i aus Länge ($ list) * -1 bis -1 $ result: append ($ result, nth ($ list, abs ($ i)));  @return $ result;  

Auch hier profitieren wir von Sass 3.3, während wir Sass 3.2 weiterhin unterstützen. Das ist ziemlich nett, findest du nicht? Natürlich können wir die Funktion auch anders herum schreiben, indem wir uns zuerst mit Sass 3.2 beschäftigen. Es macht absolut keinen Unterschied.

@ function reverse ($ list) // Wenn 'set-nth' nicht existiert (Sass 3.2) @if function-exists ('set-nth')! = true $ result: (); @für $ i aus Länge ($ list) * -1 bis -1 $ result: append ($ result, nth ($ list, abs ($ i)));  @return $ result;  // Sonst ist es Sass 3.3 @for $ i von 1 bis zum Boden (Länge ($ list) / 2) $ list: set-nth (set-nth ($ list, $ i, nth ($ list, - $ i.) )), - $ i, nth ($ list, $ i));  @return $ list;  

Hinweis: Um zu prüfen, ob wir im letzten Beispiel Sass 3.2 ausführen, hätten wir testen können function-exists ("set-nth") == unquote ('function-exists ("set-nth")') auch, aber das ist ziemlich lang und fehleranfällig.

Speichern der Sass-Version in einer Variablen

Um zu vermeiden, mehr nach vorhandenen Features zu suchen, und da wir hier nur mit zwei verschiedenen Sass-Versionen arbeiten, können wir die Sass-Version in einer globalen Variablen speichern. So habe ich es gemacht:

$ sass-version: if (Funktion existiert ("Funktion existiert") == true, 3.3, 3.2); 

Ich gebe dir das ist ziemlich schwierig. Gestatten Sie mir zu erklären, was hier los ist. Wir benutzen die ob() ternäre Funktion, die so gestaltet ist:

  • das erste Argument des ob() Funktion ist die Bedingung; es wertet sich aus wahr oder falsch
  • wenn die Bedingung zu bewertet wird wahr, es gibt das zweite Argument zurück
  • Andernfalls wird das dritte Argument zurückgegeben

Hinweis: Sass 3.2 ist ein bisschen fehlerhaft mit der ternären Funktion. Es wertet alle drei Werte aus, nicht nur den zurückzugebenden Wert. Dies kann manchmal zu unerwarteten Fehlern führen.

Schauen wir uns jetzt an, was mit Sass 3.3 los ist:

  • Funktion existiert ('Funktion existiert') kehrt zurück wahr weil offensichtlich function-exists () existiert
  • dann function-exists ('function-exists') == wahr ist wie wahr == wahr welches ist wahr
  • so $ sass-version ist eingestellt auf 3.3

Und wenn wir Sass 3.2 ausführen:

  • Funktion existiert ('Funktion existiert') ist keine Funktion, sondern eine Zeichenfolge, also im Grunde "Funktion existiert ('Funktion existiert')"
  • function-exists ('function-exists') == wahr ist falsch
  • so $ sass-version ist eingestellt auf 3.2

Wenn Sie eine Funktion wie eine Person sind, können Sie dieses Zeug in eine Funktion einschließen.

@function sass-version () @return if (function-exists ("function-exists") == wahr, 3.3, 3.2);  

Dann benutze es so:

@if sass-version () == 3.3 // Sass 3.3 @if sass-version () == 3.2 // Sass 3.2 @if sass-version () < 3.3  // Sass 3.2  

Natürlich hätten wir die Existenz einer anderen 3.3-Funktion prüfen können Anruf() oder map-get () Es könnte aber möglicherweise eine Version von Sass geben * existiert Funktionen sind implementiert, aber nicht Anruf() oder Karten, also habe ich das Gefühl, dass es besser ist, auf die Existenz eines zu überprüfen * existiert Funktion. Und da benutzen wir Funktion existiert, Lassen Sie uns dies testen!

In die Zukunft!

Sass 3.3 ist die erste Version, die implementiert wird * existiert Funktionen, also müssen wir prüfen, ob * -existiert ($ param)Gibt eigentlich einen Boolean zurück oder wird als String analysiert, was irgendwie hackig ist.

Nehmen wir an, Sass 3.4 wird morgen mit a veröffentlicht Einhorn() Funktion, die Großartigkeit und Regenbogen in die Welt bringt. Die Funktion zur Erkennung der Sass-Version würde wahrscheinlich so aussehen:

@function sass-version () @if function-exists ('unicorn') == true @return 3.4;  @else wenn Funktion existiert ('Einhorn') == false @return 3.3;  @else @return 3.2;  

Neapolitanisches Einhorn von Erin Hunting

Und wenn dann Sass 3.5 bringt Regenbogen() Funktion würden Sie aktualisieren Sass-Version () diesen Weg:

@function sass-version () @if function-exists ('rainbow') == true @return 3.5;  @else wenn Funktion existiert ('Einhorn') == wahr und Funktion vorhanden ('Regenbogen') == falsch @return 3.4;  @else wenn Funktion existiert ('Einhorn') == false @return 3.3;  @else @return 3.2;  

Und so weiter.

Apropos Einhörner und Regenbogen…

Was wäre wenn Ja wirklich Awesome wäre die Möglichkeit, eine Datei innerhalb einer bedingten Anweisung zu importieren. Leider ist dies momentan nicht möglich. Davon abgesehen, es ist für Sass 4.0 geplant, also lassen wir die Hoffnung noch nicht los.

Stellen Sie sich jedoch vor, wir könnten eine Datei basierend auf dem Ergebnis der importieren Sass-Version () Funktion. Dies macht es verdammt einfach, die Funktionen von Sass 3.3 für Sass 3.2 zu füllen.

Zum Beispiel könnten wir eine Datei mit allen Sass 3.2-Versionen von Kartenfunktionen verwenden, die stattdessen zweidimensionale Listen verwendet (wie das, was Lu Nelson mit Sass-List-Maps gemacht hat) und sie nur bei Sass 3.2 importieren sollte, z.

// Das funktioniert leider nicht :( @if sass-version () < 3.3  @import "polyfills/maps";  

Dann könnten wir alle diese Funktionen verwenden (wie map-get) in unserem Code, ohne sich um die Sass-Version zu kümmern. Sass 3.3 verwendet native Funktionen, während Sass 3.2 Polyfills verwendet. 

Aber das geht nicht.

Man könnte auf die Idee kommen, Funktionen in einer bedingten Anweisung zu definieren, anstatt eine ganze Datei zu importieren. Dann könnten wir Kartenfunktionen nur definieren, wenn sie noch nicht existieren (mit anderen Worten: Sass 3.2). Leider funktioniert das auch nicht: Funktionen und Mixins können nicht in einer Direktive definiert werden.

Funktionen können nicht in Steueranweisungen oder anderen Mixins definiert werden.

Das Beste, was wir derzeit tun können, ist die Definition einer Sass 3.2- und einer Sass 3.3-Version in jeder Funktion, wie wir oben in diesem Artikel gesehen haben. Die Wartung ist jedoch nicht nur komplizierter, sondern erfordert auch, dass jede native Sass 3.3-Funktion in eine benutzerdefinierte Funktion eingebunden ist. Schauen Sie sich unser an ersetzen-nth Funktion von früher: Wir können es nicht nennen Set-nth (), oder es wird endlos rekursiv sein, wenn Sass 3.3 verwendet wird. Wir müssen also einen benutzerdefinierten Namen finden (in diesem Fall) ersetzen-nth).

Wenn Sie Funktionen definieren oder Dateien innerhalb von bedingten Anweisungen importieren können, ist es möglich, native Funktionen beizubehalten, während Polyfills für ältere Versionen von Sass generiert werden. Leider können wir nicht. Das ist Scheiße.

In der Zwischenzeit könnten wir damit den Benutzer warnen, wenn er einen veralteten Sass-Compiler verwendet. Wenn Ihre Sass-Bibliothek / Ihr Framework / was auch immer Sass verwendet, können Sie dies über Ihr Stylesheet hinzufügen:

@if sass-version () < 3.3  @warn "You are using a version of Sass prior to 3.3. Unfortunately for you, Sass 3.3 is required for this tool to work. Please make sure to upgrade your Sass compiler.";  

Dort. Falls der Code abstürzt, weil Sie nicht unterstützte Funktionen wie Karten und ähnliches verwenden, wird der Benutzer darüber informiert, warum er die Ausgabe überprüft.

Abschließende Gedanken

Bislang hat sich Sass vom Standpunkt der Versionierung nur langsam bewegt. Ich erinnere mich, dass ich irgendwo gelesen hatte, dass Sass-Betreuer etwas schneller vorgehen wollten, was bedeutet, dass wir uns bald mit mehreren Sass-Versionen befassen könnten.

Lernen, wie man die Sass-Version erkennt und nutzt * existiert Funktion wird meines Erachtens eines Tages wichtig sein, zumindest für einige Projekte (Frameworks, Rastersysteme, Bibliotheken ...). Bis dahin, Sassing Jungs!

Lesen Sie weiter

  • Sass 3.3 (Maptastic Maple) von John W. Long
  • Sass 3.3 ist im Sass-Blog veröffentlicht