Când și cum să susținem mai multe versiuni ale Sass

A doua zi, am revizuit codul Sass al sistemului de sisteme Jeet, doar de dragul lui. După câteva comentarii cu privire la depozitul GitHub, am înțeles că administratorii lui Jeet nu erau încă pregătiți să se mute la Sass 3.3. În realitate, este mai precis să spunem Jeet utilizatori nu sunt gata să se mute la Sass 3.3, în funcție de numărul de probleme deschise când Jeet a început să utilizeze caracteristicile Sass 3.3. 

Oricum, punctul este că Jeet nu poate obține toate lucrurile reci și strălucitoare de la Sass 3.3. Sau poate?

* -exists funcţii

Dacă știți ce versiune 3.3 ați adus la Sass, s-ar putea să știți că în core au fost adăugate câteva funcții de ajutor, menite să ajute dezvoltatorii de framework să susțină simultan mai multe versiuni de Sass:

  • există la nivel mondial-variabilă (nume $): verifică dacă există o variabilă în domeniul global
  • Există variabilă (nume $): verifică dacă există o variabilă în domeniul de aplicare curent
  • Există funcția-(nume $): verifică dacă există o funcție în domeniul global
  • Există mixin-(nume $): verifică dacă există un amestec în domeniul global

Este deasemenea o Există caracteristică-(nume $) dar nu știu sigur ce face, de vreme ce documentele sunt destul de evazive. Chiar am aruncat o privire asupra codului funcției, dar nu face mai mult decâtbool (Sass.has_feature? (feature.value)), care nu ajută prea mult.

Oricum, avem câteva funcții capabile să verificăm dacă există o funcție, o mixin sau o variabilă și asta e destul de frumos. Este timpul să mergem mai departe.

Detectarea versiunii Sass

Bine, funcții noi, destul de cool. Dar ce se întâmplă când folosim una din aceste funcții într-un mediu Sass 3.2.x? Să vedem cu un mic exemplu.

// Definirea variabilei $ my-awesome-variable: 42; // Undeva în altă parte a codului $ does-my-awesome-variable-exist: variabilă-există ("my-awesome-variable"); // Sass 3.3 -> 'true' // Sass 3.2 -> 'variabilă-există (' my-awesome-variable ' 

După cum puteți vedea din rezultate, Sass 3.2 nu se prăbușește și nu aruncă nici o eroare. Se analizează variabilă există ( „mi-minunat-variabilă“) ca un șir, atât de practic „Variabilă există («mi-minunat-variabilă»)“. Pentru a verifica dacă avem de-a face cu o valoare booleană sau cu un șir, putem scrie un test foarte simplu:

$ return-type: tipul de ($ does-my-awesome-variable-exist); // Sass 3.3 -> 'bool' // Sass 3.2 -> 'șir' 

Acum putem detecta versiunea Sass din cadrul codului. Cât de minunat este asta? De fapt, nu detectăm exact versiunea Sass; mai degrabă găsim o modalitate de a defini dacă executăm Sass 3.2 sau Sass 3.3, dar asta este tot ce avem nevoie în acest caz.

Îmbunătățire progresivă

Să ne uităm la îmbunătățirea progresivă a funcțiilor Sass. De exemplu, am putea utiliza instrumentele native dacă sunt disponibile (Sass 3.3), sau să revină la cele personalizate dacă nu sunt (Sass 3.2). Asta i-am sugerat lui Jeet în ceea ce privește înlocuiți-nth () funcție, care este utilizată pentru a înlocui o valoare la un anumit index.

Iată cum ne ar putea Fă-o:

($ list, $ index, $ value) // Daca set-nth exista (Sass 3.3) @if function-exists ('set-nth') == true  nth (lista $, indexul $, valoarea $);  // Altfel este Sass 3.2 $ rezultat: (); $ index: dacă ($ 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;  

Și apoi cred că ești ca ...  care este motivul pentru a face acest lucru dacă putem face să funcționeze pentru Sass 3.2 oricum? Întrebare corectă. Eu as spune performanţă. În cazul nostru, set-nth este o funcție nativă de la Sass 3.3, ceea ce înseamnă că funcționează în Ruby, ceea ce înseamnă că este mult mai rapid decât o funcție personalizată Sass. Practic, manipulările se fac pe partea Ruby în locul compilatorului Sass.

Un alt exemplu (încă de la Jeet) ar fi a inversa , inversând o listă de valori. Când am lansat pentru prima oară SassyLists, nu a existat niciun Sass 3.3, astfel că inversarea unei liste ar însemna crearea unei noi liste, looping înapoi peste lista inițială, adăugarea de valori la cea nouă. A făcut bine treaba. Cu toate acestea, acum că avem acces la set-nth funcția de la Sass 3.3, există o modalitate mult mai bună de inversare a unei liste: schimbarea indexurilor.

Pentru a compara performanța dintre cele două implementări, am încercat să inversez de 500 de ori inversarea alfabetului latin (o listă cu 26 de articole). Rezultatele au fost, mai mult sau mai puțin:

  • între 2s și 3s cu "abordarea 3.2" (folosind adăuga)
  • niciodată de mai sus 2s cu "3.3 abordare" (folosind set-nth)

Diferența ar fi chiar mai mare cu o listă mai lungă, pur și simplu pentru că indexurile de schimbare sunt mult mai rapide decât valorile care se adaugă. Deci, încă o dată, am încercat să văd dacă am putea obține cele mai multe din cele două lumi. Iată ce am venit:

($ list) // Dacă 'set-nth' nu există (Sass 3.3) @if funcția -există ('set-nth') == true  listă) / 2) $ list: set-nth (set-nth ($ list, $ i, nth ($ list, - $ i)); - $ i, nth ($ list, $ i);  @ return $ list;  // Altfel este Sass 3.2 $ rezultat: (); @ pentru $ i din lungime ($ list) * -1 până la -1 $ rezultat: append ($ rezultat, nth ($ list, abs ($ i)));  @return $ result;  

Din nou, primim cea mai mare parte a Sass 3.3, sprijinind în același timp Sass 3.2. E destul de curat, nu crezi? Bineinteles ca am putea scrie functia invers, in primul rand cu Sass 3.2. Nu face absolut nicio diferență.

($ list) // Daca set-nth nu exista (Sass 3.2) @if function-exists ('set-nth')! = true $ result:; @ pentru $ i din lungime ($ list) * -1 până la -1 $ rezultat: append ($ rezultat, nth ($ list, abs ($ i)));  @return $ result;  // Altfel este Sass 3.3 @ pentru $ i de la 1 la etaj (lungime ($ list) / 2) $ list: set-nth (set-nth ($ list, $ i, nth )), - $ i, nth ($ list, $ i));  @ return $ list;  

Notă: pentru a verifica dacă rulați Sass 3.2 în ultimul exemplu, am fi putut fi testate function-exists ("set-nth") == unquote ('function-exists ("set-nth" de asemenea, dar aceasta este destul de lungă și predispusă la erori.

Stocarea versiunii Sass într-o variabilă

Pentru a evita verificarea de mai multe ori a funcțiilor existente și pentru că avem doar două versiuni Sass diferite, putem stoca versiunea Sass într-o variabilă globală. Iată cum am făcut-o:

$ sass-version: dacă (funcția-există ("funcția-există") == true, 3.3, 3.2); 

O să-ți dau un lucru complicat. Permiteți-mi să vă explic ce se întâmplă aici. Noi folosim dacă() funcția ternară, proiectată astfel:

  • primul argument al dacă() funcția este condiția; se evaluează Adevărat sau fals
  • dacă condiția evaluează Adevărat, returnează al doilea argument
  • altfel returnează al treilea argument

Notă: Sass 3.2 este un fel de buggy cu funcția ternară. Evaluează toate cele trei valori, nu numai pe cea care trebuie returnată. Acest lucru poate duce uneori la unele erori neașteptate.

Acum, să aruncăm o privire la ceea ce se întâmplă cu Sass 3.3:

  • Funcția-există ( „funcție-există“) se intoarce Adevărat pentru că evident Funcția-există () există
  • atunci function-exists ('function-exists') == true este ca true == true care este Adevărat
  • asa de $ Sass-versiune este setat sa 3.3

Și dacă executăm Sass 3.2:

  • Funcția-există ( „funcție-există“) nu este o funcție, ci un șir, deci practic „Funcția-există («funcția de-există»)“
  • function-exists ('function-exists') == true este fals
  • asa de $ Sass-versiune este setat sa 3.2

Dacă sunteți o funcție de fel de persoană, puteți împacheta aceste lucruri într-o funcție.

@ function sass-version () @ return dacă (funcția-există ("funcția-există") == true, 3.3, 3.2);  

Apoi, utilizați-l astfel:

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

Desigur, am fi putut verifica existența unei alte funcții de 3.3 apel() sau harta-get () dar ar putea fi o versiune a lui Sass unde * -exists funcțiile sunt implementate, dar nu apel() sau hărți, așa că simt că este mai bine să verificăm existența unui * -exists funcţie. Și din moment ce o folosim Funcția-există, să încercăm asta!

In viitor!

Sass 3.3 este prima versiune de implementat * -exists funcții, deci trebuie să verificăm dacă * -Exists ($ param)de fapt, returnează un boolean sau este analizat ca un șir, care este cam nebun.

Acum, să zicem că Sass 3.4 este lansat mâine cu un inorog() funcția, aducând minuni și curcubeu lumii. Funcția de detectare a versiunii Sass ar arăta, probabil, astfel:

@function sass-version () @ dacă funcția-există ('unicorn') == true @return 3.4;  @else dacă funcția există ('unicorn') == false @return 3.3;  @else @return 3.2;  

Neapolitanul Unicorn de Erin Hunting

Și apoi dacă Sass 3.5 aduce un a curcubeu() funcția, ați actualiza Sass-versiune () pe aici:

@funcția sass-function () @ dacă funcția-există ('rainbow') == true @return 3.5;  @else dacă funcția -există ('unicorn') == true și funcția-există ('rainbow') == false @return 3.4;  @else dacă funcția există ('unicorn') == false @return 3.3;  @else @return 3.2;  

Si asa mai departe.

Vorbind despre Unicorns și Rainbows ...

Ce va fi într-adevăr minunat ar fi capacitatea de a importa un fișier într-o declarație condiționată. Din păcate, acest lucru nu este posibil în acest moment. Acestea fiind spuse, este programat pentru Sass 4.0, așa că să nu pierdem încă speranța.

Oricum, imaginați-vă că putem importa un fișier bazat pe rezultatul Sass-versiune () funcţie. Acest lucru ar face ca jocul Sass 3.3 să funcționeze ușor pentru Sass 3.2.

De exemplu, am putea avea un fișier care să includă toate versiunile de Sass 3.2 ale funcțiilor de hartă care utilizează liste bidimensionale (precum ceea ce a făcut Lu Nelson cu Sass-List-Maps) și să le importați doar atunci când se ocupă de Sass 3.2,

// Din păcate, acest lucru nu funcționează: (@ sass-version () < 3.3  @import "polyfills/maps";  

Apoi, am putea folosi toate aceste funcții (cum ar fi harta-get) în codul nostru fără să ne îngrijorăm de versiunea Sass. Sass 3.3 ar folosi funcții native, în timp ce Sass 3.2 ar folosi polifluze. 

Dar asta nu merge.

S-ar putea veni cu ideea de a defini funcții într-o declarație condiționată, în loc să importați un întreg fișier. Apoi, am putea defini funcțiile legate de hartă numai dacă acestea încă nu există (cu alte cuvinte: Sass 3.2). Din păcate, acest lucru nu funcționează nici: funcțiile și mixurile nu pot fi definite într-o directivă.

Este posibil ca funcțiile să nu fie definite în cadrul directivelor de control sau al altor amestecuri.

Cel mai bun lucru pe care îl putem face în acest moment este definirea unei versiuni Sass 3.2 și a unei versiuni Sass 3.3 în fiecare funcție, așa cum am văzut în partea de sus a acestui articol. Dar nu numai că este mai complicat să se mențină, dar necesită, de asemenea, ca fiecare funcție nativă Sass 3.3 să fie înfășurată într-o funcție personalizată. Aruncă o privire înapoi la noi înlocuiți-nth funcția de la început: nu o putem numi set-nth (), sau va fi recursiv la nesfârșit când se utilizează Sass 3.3. Deci trebuie să găsim un nume personalizat (în acest caz înlocuiți-nth).

Fiind capabil să definească funcții sau să importe fișiere în cadrul directivelor condiționale ar face posibilă păstrarea caracteristicilor native ca atare, generând în același timp polifluze pentru versiunile mai vechi ale Sass. Din păcate, nu putem. Asta e de rahat.

Între timp, presupun că am putea folosi acest lucru pentru a avertiza utilizatorul de fiecare dată când folosește un complot Sass învechit. De exemplu, în cazul în care biblioteca dvs. Sass / cadru / orice foloseste Sass, puteți adăuga acest lucru pe partea de sus a foii de stil principale:

@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.";  

Acolo. În cazul în care codul se blochează deoarece utilizați funcții neacceptate, cum ar fi hărți și chestii, utilizatorul va fi avertizat de ce atunci când verifică ieșirea.

Gândurile finale

Până acum, Sass a fost destul de greu să se mute pe punctul de stand-up. Îmi amintesc că am citit undeva că administratorii Sass doresc să meargă mai repede, ceea ce înseamnă că ne-am putea ocupa de mai multe versiuni Sass oricând în curând.

Învățați cum să detectați versiunea Sass și să folosiți * -exists funcția va fi, după părerea mea, importantă într-o zi, cel puțin pentru unele proiecte (cadre, sisteme de rețea, biblioteci ...). Până atunci, păstrați-i pe băieții Sassing!

Citirea în continuare

  • Sass 3.3 (Maptastic Maple) de John W. Long
  • Sass 3.3 este lansat pe blogul Sass