AbsurdJS of waarom ik mijn eigen CSS Preprocessor heb geschreven

Als front-end developer schrijf ik veel CSS en het gebruik van pure CSS is tegenwoordig niet de meest efficiënte manier. CSS-preprocessors zijn iets dat me veel heeft geholpen. Mijn eerste indruk was dat ik eindelijk de perfecte tool vond. Deze hebben een heleboel functies, geweldige ondersteuning, gratis bronnen, enzovoort. Dit is allemaal waar en het is nog steeds van toepassing, maar na verschillende projecten besefte ik dat de wereld niet zo perfect is. Er zijn twee hoofd-CSS-preprocessors: LESS en SASS. Er zijn er nog meer, maar ik heb alleen ervaring met deze twee. In het eerste deel van dit artikel zal ik met u delen wat ik niet leuk vind aan preprocessors en in het tweede deel laat ik u zien hoe ik de meeste problemen die ik had, kon oplossen.


De problemen

Opstelling

Welke CSS preprocessor er ook bij betrokken is, er is altijd een setup nodig, je kunt niet zomaar beginnen met typen .minder of .sass bestanden en verwacht de .css het dossier. MINDER vereist NodeJS en SASS Ruby. Op dit moment werk ik voornamelijk aan HTML / CSS / JavaScript / NodeJS-applicaties. Dus LESS lijkt een betere optie, omdat ik geen extra software hoef te installeren. Weet u, het toevoegen van één ding aan uw ecosysteem betekent meer tijd voor onderhoud. Bovendien hebt u niet alleen de vereiste tool nodig, maar al uw collega's moeten nu ook het nieuwe instrument integreren.

Ten eerste heb ik voor LESS gekozen omdat ik NodeJS al had laten installeren. Het speelde goed met Grunt en ik heb met succes twee projecten voltooid met die opstelling. Daarna begon ik te lezen over SASS. Ik was geïnteresseerd in OOCSS, Atomic design en ik wilde een solide CSS-architectuur bouwen. Al snel stapte ik over naar SASS, omdat het me betere mogelijkheden gaf. Natuurlijk moest ik (en ook mijn collega's) Ruby installeren.

uitgang

Veel ontwikkelaars controleren de geproduceerde CSS niet. Ik bedoel, je hebt misschien echt goed uitziende SASS-bestanden, maar wat er uiteindelijk wordt gebruikt, is de gecompileerde .css het dossier. Als het niet is geoptimaliseerd en de bestandsgrootte hoog is, hebt u een probleem. Er zijn een paar dingen die ik niet leuk vind in beide preprocessors.

Laten we zeggen dat we de volgende code hebben:

// MINDER of SASS p font-size: 20px;  p opvulling: 20px; 

Denk je niet dat dit moet worden gecompileerd om:

p font-size: 20px; opvulling: 20px; 

Noch MINDER noch SASS werkt zo. Ze laten je stijlen gewoon terwijl je typt. Dit kan leiden tot codeduplicatie. Wat als ik complexe architectuur met meerdere lagen heb en elke laag iets toevoegt aan de alinea. Er zullen verschillende definities zijn, die niet precies nodig zijn. Je hebt misschien zelfs de volgende situatie:

p font-size: 20px;  p font-size: 30px; 

De juiste code aan het einde zou alleen het volgende moeten zijn:

p font-size: 30px; 

Ik weet nu dat de browser voorzichtig zal zijn en de juiste lettergrootte zal vinden. Maar is het niet beter om die operaties te redden. Ik weet niet zeker of dit van invloed is op de prestaties van uw pagina, maar het beïnvloedt de leesbaarheid zeker.

Het combineren van selectors die dezelfde stijlen delen, is een goede zaak. Voor zover ik weet, doet LESS dit niet. Laten we zeggen dat we een mix hebben en we willen het toepassen op twee klassen.

.reset () opvulling: 0; marge: 0;  .header .reset ();  .footer .reset (); 

En het resultaat is:

.header opvulling: 0; marge: 0;  .footer opvulling: 0; marge: 0; 

Deze twee klassen hebben dus dezelfde stijlen en kunnen in één definitie worden gecombineerd.

.header, .footer padding: 0; marge: 0; 

Ik vroeg me af of dit daadwerkelijke prestatie-optimalisatie is en ik heb geen juist antwoord gevonden, maar het lijkt me een goede zaak. SASS heeft iets genaamd plaats houders. Het wordt precies voor dergelijke situaties gebruikt. Bijvoorbeeld:

% reset opvulling: 0; marge: 0;  .header @extend% reset;  .footer @extend% reset; 

De bovenstaande code produceert precies wat ik wilde. Het probleem is dat als ik te veel plaatshouders gebruik, ik veel stijldefinities kan krijgen, omdat de preprocessor denkt dat ik iets te combineren heb.

% reset opvulling: 0; marge: 0; % gegrenst border: solid 1px # 000; % box display: block; opvulling: 10px;  .header @extend% reset; @extend% begrensd; @extend% box; 

Er zijn drie plaatshouders. De .hoofd class breidt ze allemaal uit en de uiteindelijke gecompileerde CSS ziet er als volgt uit:

.header opvulling: 0; marge: 0;  .header border: solid 1px # 000;  .header display: block; opvulling: 10px; 

Het ziet er verkeerd uit, is het niet? Er zou maar één stijldefinitie en slechts één moeten zijn vulling eigendom.

.header opvulling: 10px; marge: 0; rand: vast 1px # 000; weergave: blok; 

Natuurlijk zijn er hulpmiddelen die dit kunnen oplossen, met de gecompileerde CSS. Maar zoals ik al zei, geef ik er de voorkeur aan als minder bibliotheken te gebruiken.

Syntaxisbeperking

Terwijl ik aan OrganicCSS werkte, ontmoette ik veel beperkingen. Over het algemeen wilde ik CSS schrijven terwijl ik JavaScript schrijf. Ik bedoel, ik had wat ideeën over complexe architectuur, maar ik kon ze niet bereiken, omdat de taal waarmee ik werkte nogal primitief was. Laten we zeggen dat ik een mix nodig heb die mijn elementen stijlen. Ik wil een thema en een border type doorgeven. Hier is hoe dit eruit zou moeten zien in MINDER:

.theme-dark () color: #FFF; achtergrond: # 000;  .theme-light () color: # 000; achtergrond: #FFF;  .component (@theme, @border) border: "@ border 1px # F00"; .theme - @ thema ();  .header .component ("dark", "dotted"); 

Natuurlijk zal ik veel thema's hebben en het zouden ook mixins moeten zijn. Dus de variabele interpolatie werkt voor de eigenschap border, maar niet voor de mixin-namen. Dat is een eenvoudige, maar het is op dit moment niet mogelijk, of ik weet tenminste niet of het is opgelost. Als u de bovenstaande code probeert te compileren, krijgt u Syntaxisfout op regel 11.

SASS is nog een stap verder. De interpolatie werkt met tijdelijke aanduidingen, wat het een beetje beter maakt. Hetzelfde idee ziet er als volgt uit:

@mixin theme-dark () color: #FFF; achtergrond: # 000;  @mixin theme-light () color: # 000; achtergrond: #FFF; % border-dotted border: gestippeld 1px # 000;  @mixin component ($ theme, $ border) @extend% border - # $ border; @include thema - # $ theme;  .header @include component ("dark", "dotted"); 

De borderstyling werkt dus, maar het thema produceert:

Sass-fout: ongeldige CSS na "@include theme-": expected "", was "# $ theme;"

Dat komt omdat de interpolatie in de namen van de mixins en uitbreidingen niet is toegestaan. Daar is een lange discussie over en waarschijnlijk zal dit binnenkort worden opgelost.

Zowel LESS als SASS zijn geweldig als u uw schrijfsnelheid wilt verbeteren, maar ze zijn verre van perfect voor het bouwen van modulaire en flexibele CSS. Voornamelijk missen ze dingen als inkapseling, polymorfisme en abstractie. Of tenminste, ze zijn niet in de vorm die ik nodig had.


Een nieuwe aanpak

Ik vocht meerdere dagen met die beperking. Ik heb veel tijd geïnvesteerd in het lezen van documentatie. Uiteindelijk gaf ik het gewoon op en ging op zoek naar andere opties. Wat ik had was niet flexibel genoeg en ik begon te denken aan het schrijven van mijn eigen preprocessor. Natuurlijk, dat is een heel complexe taak en er zijn veel dingen om over na te denken, zoals:

  • de invoer - Normale preprocessors nemen code die lijkt op CSS. Ik denk dat het de bedoeling is om de taal aan te vullen, zoals het toevoegen van ontbrekende, maar toch noodzakelijke functies. Het is ook eenvoudig om pure CSS te porten en de ontwikkelaars kunnen het meteen gaan gebruiken, omdat het in de praktijk bijna dezelfde taal is. Vanuit mijn oogpunt brengt deze benadering echter weinig moeilijkheden met zich mee, omdat ik alles moest analyseren en analyseren.
  • de syntaxis - zelfs als ik het parseergedeelte schrijf, moest ik mijn eigen syntax uitvinden, wat nogal een complexe klus is.
  • concurrenten - er zijn al twee echt populaire preprocessors. Ze hebben goede ondersteuning en een actieve gemeenschap. Weet je, de meeste van de coolste dingen in onze sfeer zijn zo nuttig, vanwege de bijdragers. Als ik mijn eigen CSS preprocessor schrijf en ik krijg niet genoeg feedback en ondersteuning van de mensen, ben ik misschien de enige die het daadwerkelijk gebruikt.

Dus, ik heb er een beetje over nagedacht en een oplossing gevonden. Het is niet nodig om een ​​nieuwe taal uit te vinden met een nieuwe syntaxis. Het is er al. Ik zou pure JavaScript kunnen gebruiken. Er is al een grote community en veel mensen kunnen mijn bibliotheek meteen gaan gebruiken. In plaats van externe bestanden te lezen, te ontleden en te compileren, besloot ik om het NodeJS-ecosysteem te gebruiken. En natuurlijk het allerbelangrijkste: ik heb het CSS-gedeelte volledig verwijderd. Het schrijven van alles in JavaScript maakte mijn webapplicatie een stuk schoner, omdat ik niet te maken had met het invoerformaat en al die processen die de eigenlijke CSS produceren.

(De naam van de bibliotheek is AbsurdJS. Misschien vindt u deze naam grappig en dat is het inderdaad. Toen ik mijn idee met enkele vrienden deelde, zeiden ze allemaal:, het schrijven van uw CSS in JavaScript - absurd. Dus dat was de perfecte titel.)


AbsurdJS

Installatie

Om AbsurdJS te gebruiken, moet je NodeJS geïnstalleerd hebben. Als je dit juweeltje nog steeds niet op je systeem hebt, ga dan naar nodejs.org en klik op Installeren knop. Zodra alles is voltooid, kunt u een nieuwe console openen en typen:

npm install -g absurd

Hiermee wordt AbsurdJS wereldwijd geïnstalleerd. Dit betekent dat waar u ook bent, u het absurd commando.

Uw CSS schrijven

In de JavaScript-wereld komt CSS het dichtst in de buurt van het JSON-formaat. Dus dat is wat ik besloot te gebruiken. Laten we een eenvoudig voorbeeld nemen:

.inhoud opvulling: 0; marge: 0; lettertypegrootte: 20px;  .content p line-height: 30px; 

Dit is pure CSS. Hier is hoe het eruit ziet in MINDER en SASS:

.inhoud opvulling: 0; marge: 0; lettertypegrootte: 20px; p line-height: 30px; 

In de context van AbsurdJS zou het fragment zo moeten worden geschreven:

module.exports = function (api) api.add ('.content': padding: 0, margin: 0, 'font-size': '20px', p: 'line-height': '30px' ); 

U kunt dit opslaan in een bestand met de naam styles.js en loop:

absurd -s. \ styles.js

Het zal de JavasSript naar dezelfde CSS compileren. Het idee is eenvoudig. U schrijft een NodeJS-pakket, dat een functie exporteert. De functie wordt aangeroepen met slechts één parameter: de AbsurdJS API. Het heeft verschillende methoden en ik zal ze later doornemen, maar de meest gebruikelijke is toevoegen. Het accepteert geldige JSON. Elk object definieert een selector. Elke eigenschap van dat object kan een CSS-eigenschap en de bijbehorende waarde of een ander object zijn.

Importeren

Het plaatsen van verschillende delen van uw CSS in verschillende bestanden is erg belangrijk. Deze aanpak verbetert de leesbaarheid van uw stijlen. AbsurdJS heeft een importeren methode, die fungeert als de @importeren richtlijn in de CSS-preprocessors.

var cwd = __dirname; module.exports = function (api) api.import (cwd + '/config/main.js'); api.import (cwd + '/config/theme-a.js'); api.import ([cwd + '/layout/grid.js', cwd + '/forms/login-form.js', cwd + '/forms/feedback-form.js']); 

Wat je moet doen is een schrijven main.js bestand dat de rest van de stijlen importeert. Je moet weten dat er overschrijft. Wat ik bedoel is dat als je een stijl definieert voor de lichaam tag binnen /config/main.js en later in /config/theme-a.js gebruik dezelfde eigenschap, de laatste waarde is de waarde die is gebruikt in het laatste geïmporteerde bestand. Bijvoorbeeld:

module.exports = function (api) api.add (body: margin: '20px'); api.add (body: margin: '30px'); 

Wordt gecompileerd met

body margin: 30px; 

Merk op dat er maar één selector is. Terwijl, als je hetzelfde doet in MINDER of SASS je krijgt

body margin: 20px;  body margin: 30px; 

Variabelen en mixins

Een van de meest waardevolle functies in preprocessors zijn hun variabelen. Ze bieden u de mogelijkheid om uw CSS te configureren, zoals een instelling ergens aan het begin van het stylesheet definiëren en later gebruiken. In JavaScript zijn variabelen iets normaals. Omdat u modules in verschillende bestanden hebt geplaatst, heeft u echter iets nodig dat fungeert als een brug tussen deze modules. Misschien wilt u uw hoofdkleur van het merk in één bestand definiëren, maar later in een ander bestand gebruiken. AbsurdJS biedt hiervoor een API-methode aan opslagruimte. Als u de functie uitvoert met twee parameters, maakt u een paar: sleutel waarde. Als u alleen een sleutel doorgeeft, krijgt u daadwerkelijk de opgeslagen waarde.

// config.js module.exports = function (api) api.storage ("brandColor", "# 00F");  // header.js module.exports = function (api) api.add (header: color: api.storage ("brandColor"))

Elke selector accepteert niet alleen een object, maar ook een array. Dus dit is ook geldig:

module.exports = function (api) api.add (header: [color: '# FF0', 'font-size': '20px'])

Dit maakt het mogelijk om meerdere objecten naar specifieke selectors te verzenden. Het speelt heel goed met het idee van mixins. Per definitie is de mixin een klein stukje code dat meerdere keren kan worden gebruikt. Dat is het tweede kenmerk van LESS en SASS, wat ze aantrekkelijk maakt voor ontwikkelaars. In AbsurdJS zijn de mixins eigenlijk normale JavaScript-functies. De mogelijkheid om dingen op te bergen, geeft je de mogelijkheid om mixins tussen de bestanden te delen. Bijvoorbeeld:

// A.js module.exports = function (api) api.storage ("knop", functie (kleur, dikte) return color: color, display: "inline-block", opvulling: "10px 20px", border: "solid" + thickness + "px" + color, 'font-size': "10px");  // B.js module.exports = functie (api) api.add ('. Header-knop': [api.storage ("knop") ("# AAA", 10), color: '# F00 ',' font-size ':' 13px ']); 

Het resultaat is:

.koptekstknop color: # F00; weergave: inline-block; opvulling: 10px 20px; rand: vast 10px #AAA; lettergrootte: 13px; 

Merk op dat er maar één selector gedefinieerd is en de lettertypegrootte eigenschap heeft de waarde van het tweede object in de array (de mixin definieert een aantal basisstijlen, maar later worden ze gewijzigd).

plugins

Ok, mixins zijn cool, maar ik wilde altijd mijn eigen CSS-eigenschappen definiëren. Ik bedoel met behulp van eigenschappen die normaal niet bestaan, maar inkapselen van geldige CSS-stijlen. Bijvoorbeeld:

.header text: medium; 

Laten we zeggen dat we drie soorten tekst hebben: klein, medium en groot. Elk van hen heeft een andere lettertypegrootte en anders lijnhoogte. Het is duidelijk dat ik hetzelfde kan bereiken met mixins, maar AbsurdJS biedt iets beters - plug-ins. Het maken van de plug-in gebeurt opnieuw via de API:

api.plugin ("text", functie (api, type) switch (type) case "small": return 'font-size': '12px', 'line-height': '16px' break; case "medium": return 'font-size': '20px', 'line-height': '22px' break; case 'big': return 'font-size': '30px', 'line-height' : '32px' pauze;);

Hiermee kunt u zich aanmelden tekst: medium naar uw selectors. De bovenstaande styling is samengesteld om:

.header font-size: 20px; regelhoogte: 22 px; 

Media Queries

Natuurlijk ondersteunt de bibliotheek mediaquery's. Ik heb ook het idee gekopieerd van de bubbling functie (je kunt breakpoints direct in de elementen definiëren en AbsurdJS zorgt voor de rest).

api.add ('.footer': 'font-size': '14px', '@media all en (min-width: 320px) and (max-width: 550px)': 'font-size': '24px', '.content': '@media all en (min-width: 320px) and (max-width: 550px)': margin: '24px')

Het resultaat is:

.footer font-size: 14px;  @media all en (min-width: 320px) and (max-width: 550px) .footer font-size: 24px;  .content margin: 24px; 

Houd er rekening mee dat als u dezelfde media-query meerdere keren gebruikt, het gecompileerde bestand slechts één definitie bevat. Dit bespaart eigenlijk heel veel bytes. Helaas doen LESS en SASS dit niet.

Pseudo-lessen

Hiervoor hoeft u alleen geldige JSON in te leveren. In het volgende voorbeeld ziet u hoe u pseudo-CSS-klassen gebruikt:

module.exports = function (api) api.add (a: 'text-decoration': 'none', ': hover': 'text-decoration': 'underline'); 

En het is samengesteld om:

een text-decoration: none;  a: hover text-decoration: onderstrepen; 

integratie

AbsurdJS werkt als een opdrachtregelhulpprogramma, maar het kan ook in een NodeJS-toepassing worden gebruikt. Bijvoorbeeld:

var Absurd = vereisen ("absurd"), absurd = Absurd (), api = absurd.api, output = "./css/styles.css"; api.add (...). import ("..."); absurd.compileFile (output, function (err, css) // doe iets met de css);

Of als u een bestand heeft dat fungeert als een toegangspunt:

var Absurd = vereisen ("absurd"); Absurd ("./ css / styles.js"). CompileFile ("./ css / styles.css", functie (err, css) // doe iets met de css);

De bibliotheek ondersteunt ook integratie met Grunt. U kunt hierover meer lezen op de volgende Github-pagina.

Commandoregelinterfaceopties

Er zijn drie parameters beschikbaar:

  • [-S] - hoofdbronbestand
  • [-O] - uitvoerbestand
  • [-W] - map om te bekijken

De volgende regel start bijvoorbeeld een watcher voor een ./ css map, zal grijpen ./css/main.js als een startpunt en zal het resultaat uitvoeren naar ./styles.css:

absurd -s ./css/main.js -o ./styles.css -w ./css

Conclusie

Begrijp me niet verkeerd. De beschikbare CSS-preprocessors zijn geweldig en ik gebruik ze nog steeds. Ze kwamen echter met hun eigen problemen. Ik heb ze kunnen oplossen door AbsurdJS te schrijven. De waarheid is dat ik de ene tool gewoon heb vervangen door een andere. Het gebruik van deze bibliotheek elimineert het gebruikelijke schrijven van CSS en maakt dingen echt flexibel, omdat alles JavaScript is. Het kan worden gebruikt als een opdrachtregelhulpprogramma of het kan rechtstreeks in de code van de toepassing worden geïntegreerd. Als je geïnteresseerd bent in AbsurdJS, bekijk dan de volledige documentatie op github.com/krasimir/absurd of vul de repo in.