Stuur is populair aan het worden met zijn goedkeuring in frameworks zoals Meteor en Ember.js, maar wat er echt gebeurt achter de schermen van deze opwindende sjablonengine?
In dit artikel zullen we een diepgaand kijkje nemen in het onderliggende proces. Sturen doorloopt om uw sjablonen te compileren.
Dit artikel verwacht dat je mijn vorige introductie voor hebt gelezen Stuur en veronderstelt als zodanig dat u de basiskennis van het maken van stuursjablonen kent.
Wanneer u een stuursjabloon gebruikt, weet u waarschijnlijk dat u begint met het compileren van de bron van de sjabloon in een functie met Handlebars.compile ()
en dan gebruik je die functie om de laatste HTML te genereren, waarbij waarden worden doorgegeven voor eigenschappen en tijdelijke aanduidingen.
Maar die ogenschijnlijk eenvoudige compileerfunctie doet eigenlijk nogal wat stappen achter de schermen, en dat is waar dit artikel echt over gaat; laten we een korte analyse van het proces bekijken:
In dit artikel zullen we een tool bouwen om stuursjablonen te analyseren bij elk van deze stappen, dus om de resultaten wat beter weer te geven op het scherm, zal ik de prism.js syntaxis markeerstift gemaakt door de enige echte Lea Verou. Download de geminimaliseerde bron en onthoud dat je JavaScript moet controleren in de sectie talen.
De volgende stap is om een leeg HTML-bestand te maken en dit te vullen met het volgende:
Handlebars.js tokens:
Activiteiten:
Output:
Functie:
Het is gewoon een boilerplate code die stuur en prisma bevat en vervolgens een aantal div's voor de verschillende stappen. Onderaan ziet u twee scriptblokken: de eerste is voor de sjabloon en de tweede is voor onze JS-code.
Ik heb ook een beetje CSS geschreven om alles een beetje beter te regelen, wat je vrij kunt toevoegen:
body marge: 0; opvulling: 0; font-family: "opensans", Arial, sans-serif; achtergrond: # F5F2F0; lettergrootte: 13px; #analysis top: 0; links: 0; positie: absoluut; breedte: 100%; hoogte: 100%; marge: 0; opvulling: 0; #analysis div width: 33.33%; hoogte: 50%; zweven: links; opvulling: 10px 20px; box-sizing: border-box; overloop: automatisch; #function width: 100%! important;
Vervolgens hebben we een sjabloon nodig, dus laten we beginnen met de eenvoudigste sjabloon die mogelijk is, alleen wat statische tekst:
Als u deze pagina in uw browser opent, moet de sjabloon zoals verwacht in de uitvoerbox worden weergegeven, nog niets anders. We moeten nu de code schrijven om het proces in elk van de andere drie fasen te analyseren..
De eerste stap die het stuur op uw sjabloon uitvoert, is het tokeniseren van de bron. Dit betekent dat we de bron moeten opdelen in de afzonderlijke componenten zodat we elk stuk op de juiste manier kunnen verwerken. Als er bijvoorbeeld een tekst met een tijdelijke aanduiding in het midden was, zou het Stuur de tekst scheiden voordat de tijdelijke aanduiding deze in één token zou plaatsen, waarna de tijdelijke aanduiding zelf in een ander token zou worden geplaatst en als laatste alle tekst achter de tijdelijke aanduiding. zou in een derde token worden geplaatst. Dit komt omdat die stukjes zowel de volgorde van de sjabloon moeten behouden, maar ook anders moeten worden verwerkt.
Dit proces wordt gedaan met behulp van de Handlebars.parse ()
functie, en wat u terugkrijgt, is een object dat alle segmenten of 'statements' bevat.
Om beter te illustreren waar ik het over heb, laten we een lijst met paragrafen maken voor elk van de tokens die zijn uitgenomen:
// Display Tokens var tokenizer = Handlebars.parse (src); var tokenStr = ""; for (var i in tokenizer.statements) var token = tokenizer.statements [i]; tokenStr + = ""+ (parseInt (i) +1) +") "; switch (token.type) case" content ": tokenStr + =" [string] - \ "" + token.string + "\" "; pauze; case "snor": tokenStr + = "[placeholder] -" + token.id.string; break; case "block": tokenStr + = "[block] -" + token.mustache.id.string; document. getElementById ("tokens"). innerHTML + = tokenStr;
Dus beginnen we met het openen van de sjablonenbron Handlebars.parse
om de lijst met tokens te krijgen. Vervolgens doorlopen we alle afzonderlijke componenten en bouwen we een set menselijk leesbare reeksen op, gebaseerd op het type van het segment. Platte tekst zal een type "inhoud" hebben, die we dan alleen de string kunnen exporteren die tussen aanhalingstekens is gewikkeld om te laten zien wat deze is. Placeholders hebben een soort 'snor' die we vervolgens kunnen weergeven samen met hun 'id' (naam voor placeholder). En last but not least, blokkeren helpers hebben een soort van "blok" dat we dan ook gewoon de blokken intern "id" kunnen tonen (bloknaam).
Als u dit nu in de browser vernieuwt, zou u slechts één 'tekenreeks' moeten zien, met de tekst van onze sjabloon.
Zodra het stuur de verzameling tokens heeft, bladert het door elk blok en 'genereert' een lijst met vooraf gedefinieerde bewerkingen die moeten worden uitgevoerd voor de sjabloon die moet worden gecompileerd. Dit proces wordt gedaan met behulp van de Handlebars.Compiler ()
object, doorgeven van het token-object uit stap 1:
// Weergavebewerkingen var opSequence = nieuwe Handlebars.Compiler (). Compile (tokenizer, ); var opStr = ""; for (var i in opSequence.opcodes) var op = opSequence.opcodes [i]; opStr + = ""+ (parseInt (i) +1) +") - "+ op.opcode; document.getElementById (" bewerkingen "). innerHTML + = opStr;
Hier verzamelen we de tokens in de bewerkingsreeks waarover ik sprak, en dan fietsen we door elk van hen en creëren we een vergelijkbare lijst als in de eerste stap, behalve dat we alleen de opcode hoeven te printen. De opcode is de "bewerking" of de "naam" van de functie die moet worden uitgevoerd voor elk element in de reeks.
Terug in de browser zou je nu alleen een enkele bewerking 'appendContent' moeten zien die de waarde zal toevoegen aan de huidige 'buffer' of 'reeks van tekst'. Er zijn veel verschillende opcodes en ik denk niet dat ik gekwalificeerd ben om een paar van deze opcodes uit te leggen, maar als je snel zoekt in de broncode van een bepaalde opcode, zie je de functie die ervoor wordt uitgevoerd.
De laatste stap is om de lijst met opcodes te nemen en deze om te zetten in een functie, dit doet u door de lijst met bewerkingen te lezen en de code slim aan elkaar te koppelen. Hier is de code die nodig is om bij deze functie te komen:
// Display Functie var outputFunction = new Handlebars.JavaScriptCompiler (). Compile (opSequence, , undefined, true); document.getElementById ("bron"). innerHTML = outputFunction.toString (); Prism.highlightAll ();
De eerste regel maakt de compilator door in de op-volgorde, en deze regel retourneert de laatste functie die is gebruikt voor het genereren van de sjabloon. Vervolgens converteren we de functie naar een tekenreeks en laten Prism deze syntax markeren.
Met deze definitieve code zou uw pagina er ongeveer zo uit moeten zien:
Deze functie is ongelooflijk eenvoudig, omdat er maar één bewerking is, het geeft alleen de gegeven string terug; laten we nu eens kijken naar het bewerken van de sjabloon en zien hoe deze individueel rechttoe rechtaan stappen samenkomen en een zeer krachtige abstractie vormen.
Laten we beginnen met iets eenvoudigs, en laten we gewoon het woord 'Wereld' vervangen door een tijdelijke aanduiding; uw nieuwe sjabloon moet er als volgt uitzien:
En vergeet niet om de variabele door te geven zodat de output er goed uitziet:
// Weergave-uitvoer var t = Handlebars.compile (src); document.getElementById ("uitvoer"). innerHTML + = t (name: "Gabriel");
Als u dit uitvoert, zult u ontdekken dat door slechts één eenvoudige plaatsaanduider toe te voegen, het proces nogal ingewikkeld wordt.
De ingewikkelde if / else-sectie is omdat het niet weet of de tijdelijke aanduiding in feite een tijdelijke aanduiding of een hulpmethode is
Als je nog steeds niet zeker weet wat tokens zijn, zou je nu een beter idee moeten hebben; zoals je op de afbeelding kunt zien, splitste het de tijdelijke aanduiding van de tekenreeksen en creëerde drie individuele componenten.
Vervolgens zijn er in het gedeelte bewerkingen nogal wat toevoegingen. Als je van tevoren onthoudt, om gewoon wat tekst af te geven, gebruikt Handlebars de 'appendContent'-bewerking, wat je nu bovenaan en onderaan de lijst kunt zien (voor zowel' Hallo 'als'! '). De rest in het midden bevat alle bewerkingen die nodig zijn om de tijdelijke aanduiding te verwerken en de escapecontent toe te voegen.
Tot slot, in het onderste venster, in plaats van alleen een string terug te geven, maakt deze keer een buffervariabele aan, en verwerkt hij tegelijkertijd één token. De ingewikkelde if / else-sectie is omdat het niet weet of de tijdelijke aanduiding in feite een tijdelijke aanduiding of een hulpmethode is. Dus het probeert te zien of een hulpmethode met de opgegeven naam bestaat, in welk geval het de hulpmethode zal aanroepen en 'stack1' op de waarde zet. Als het een tijdelijke aanduiding is, wijst het de waarde toe aan de ingevoerde context (hier met de naam 'depth0') en als een functie is doorgegeven, wordt het resultaat van de functie in de variabele 'stack1' geplaatst. Als dat eenmaal is gebeurd, ontsnapt het zoals we zagen in de bewerkingen en voegt het toe aan de buffer.
Laten we voor onze volgende wijziging gewoon dezelfde sjabloon proberen, behalve deze keer zonder te ontsnappen aan de resultaten (hiervoor voegt u nog een accolade toe "naam"
)
Als u de pagina vernieuwt, ziet u nu dat de bewerking is verwijderd om aan de variabele te ontsnappen en in plaats daarvan wordt de bewerking eenvoudig toegevoegd. Dit springt naar beneden in de functie die nu eenvoudig wordt gecontroleerd om er zeker van te zijn dat de waarde geen valswaarde is (naast 0) en vervolgens voegt eraan toe zonder eraan te ontsnappen.
Dus ik denk dat tijdelijke aanduidingen behoorlijk rechttoe rechtaan zijn, laten we nu eens kijken naar het gebruik van hulpfuncties.
Het heeft geen zin om dit ingewikkelder te maken dan het moet zijn, laten we gewoon een eenvoudige functie maken die het duplicaat van een ingevoerd nummer retourneert, dus vervang de sjabloon en voeg een nieuw scriptblok toe voor de helper (vóór de andere code ):
Ik heb besloten om er niet aan te ontsnappen, omdat het de laatste functie wat eenvoudiger maakt om te lezen, maar je kunt beide proberen als je wilt. In elk geval zou het uitvoeren van dit het volgende moeten opleveren:
Hier kun je zien dat het weet dat het een helper is, dus in plaats van 'invokeAmbiguous' te zeggen, staat er nu 'invokeHelper' en daarom is er ook in de functie geen if / else-blok meer. Het zorgt er echter nog steeds voor dat de helper bestaat en probeert terug te vallen naar de context voor een functie met dezelfde naam in het geval dat het niet gebeurt.
Een ander ding dat de moeite van het vermelden waard is, is dat je de parameters voor helpers kunt zien die direct worden doorgegeven, en waar mogelijk hard gecodeerd zijn, wanneer de functie wordt gegenereerd (het getal 3 in de verdubbelde functie).
Het laatste voorbeeld dat ik wil behandelen gaat over blokkeerhelpers.
Blokkeer helpers kunnen u andere tokens in een functie wikkelen die in staat is om zijn eigen context en opties in te stellen. Laten we een voorbeeld bekijken met de standaard 'if' block helper:
Hier controleren we of "naam" is ingesteld in de huidige context, in welk geval we het zullen weergeven, anders produceren we "Wereld!". Als u dit in onze analyser uitvoert, ziet u slechts twee tokens, ook al zijn er meer; dit komt omdat elk blok wordt uitgevoerd als zijn eigen 'sjabloon', dus alle tokens erin (zoals naam
) maakt geen deel uit van de buitenste oproep en u zou deze moeten extraheren uit het knooppunt van het blok zelf.
Daarnaast, als je de functie bekijkt:
U kunt zien dat het de functies van de blokhulpprogramma daadwerkelijk compileert in de functie van de sjabloon. Er zijn er twee omdat de ene de hoofdfunctie is en de andere de inverse functie (voor wanneer de parameter niet bestaat of false is). De hoofdfunctie: "programma1" is precies wat we eerder hadden toen we gewoon wat tekst en een enkele plaatshouder hadden, omdat zoals ik al zei, elk van de blokhulpfuncties precies zo opgebouwd en behandeld wordt als een standaardsjabloon. Ze worden vervolgens door de "als" -helper gereden om de juiste functie te ontvangen die deze vervolgens aan de buitenbuffer toevoegt.
Zoals eerder vermeld, is het de moeite waard om te vermelden dat de eerste parameter voor een blokkenhulp de sleutel zelf is, terwijl de 'dit' parameter is ingesteld op de volledige doorgegeven context, wat handig kan zijn bij het bouwen van je eigen blokopladers..
In dit artikel hebben we misschien niet eens een praktische blik geworpen op het bereiken van iets in Handlebars, maar ik hoop dat je een beter inzicht hebt gekregen in wat er precies achter de schermen gebeurt, waardoor je betere sjablonen en helpers kunt bouwen met deze nieuwe gevonden kennis.
Ik hoop dat je het leuk vond om te lezen, zoals altijd als je vragen hebt, neem dan gerust contact met me op via Twitter (@GabrielManricks) of op de Nettuts + IRC (#nettuts on freenode).