Revizuirea motoarelor de șabloane JS actuale. Exemple de randare de bază

Există mai multe cazuri în care este posibil să aveți nevoie de un motor de șablon JavaScript, inclusiv necesitatea de a genera conținut pe client și pe server dacă utilizați cadre JavaScript precum NodeJS sau Rhino. Voi spune imediat că am luat în considerare multe motoare de șabloane existente pentru mine, de la simplu la exotic. Cel mai mult m-au interesat cele simple, dar care să permită utilizarea logicii complexe în șabloane, iar EJS s-a dovedit a fi unul dintre ele. Totuși, chestia asta a fost scrisă oarecum altfel decât mi-aș fi dorit, pe de o parte, erau multe lucruri inutile, pe de altă parte, funcționalitatea principală era prea complicată. Am văzut o oportunitate de a face compilarea și redarea șabloanelor sale mult mai ușoară.

Aplicarea EJS

Ce face motorul de șablon: luând un șablon și date, se întoarce instanță de conținut, obtinut prin prelucrare șablon pe baza acestui specific instanță de date.

Cu EJS, ideea este simplă: ca parte a unui șablon, putem combina conținutul așa cum este cu biți de JavaScript inline, similar cu modul în care fișierele script PHP combină HTML cu cod. Codul arată atât variabilele globale, cât și pe cele pe care vom avea grijă să le transmitem la redator atunci când sunt apelate. De asemenea, puteți vedea așa-numitele funcții de ajutor care simplifică formarea structurilor utilizate frecvent, de exemplu, etichete de link sau imagini.

Codul este încadrat într-o etichetă sau etichetă.

Iată cum ar putea arăta:

Am scris în mod deliberat constructele în diferite stiluri pentru a arăta că prezența spațiilor nu contează. Când generăm rezultate folosind un șablon, transmitem un obiect cu proprietăți care acționează ca variabile pe care codul din șablonul nostru operează. Ideea este că, cu ajutorul constructelor logice din JavaScript în sine, puteți schimba în mod flexibil tipul de conținut generat ca rezultat. Ca opțiune, este posibil să folosiți etichete precum [% %] dacă șablonul ar trebui să fie randat după ce este inclus în arborele DOM. În lumina tiparelor mele de utilizare, nu mi s-a părut foarte util.

Îmbunătățirea EJS

În motorul original de șabloane, nu am fost mulțumit de funcționalitatea suplimentară pentru încărcarea șabloanelor din fișiere, de care practic nu aveam nevoie, deoarece trebuia să fie folosit în medii de server, nu doar pe partea clientului. De asemenea, am fost neplăcut surprins de complexitatea compilatorului în sine, deoarece prefer utilizarea apelurilor native complexității logicii codului. Din aceste motive, am decis să scriu un compilator de la zero, despre care vom vorbi în continuare.

Analizarea șabloanelor sursă

Compilarea începe întotdeauna cu analizarea materialului sursă în conformitate cu reguli predeterminate. Setul nostru de reguli se rezumă la a face distincția între conținutul din etichete speciale și orice altceva. Privind structura etichetelor noastre speciale, puteți vedea că este destul de ușor să le capturați în întregime folosind o expresie regulată simplă. Ne amintim că, pe lângă codul în sine, va trebui să scoatem eticheta de deschidere pentru a înțelege ce să facem cu codul din interior. Iată cum va arăta RegExp:

/(?:\n\s*)?(])+)%>/gm

Deci ce facem aici:

gm - activați procesarea globală a textului integral (cu mai multe linii). După cum puteți vedea, totul este destul de banal, tot ce rămâne este să vă dați seama ce să faceți cu această expresie obișnuită. Și aici ne va ajuta metoda foarte utilă String.split, care împarte un șir folosind delimitatorul specificat, returnând o matrice a subșirurilor rezultate. Particularitate această metodă este că poate accepta ca delimitator, iar dacă conține submodele capturate, subșirurile care le corespund, de asemenea, vor ajunge în matricea rezultată în ordinea obișnuită. Acest lucru ne dă ideea optimizării native a compilatorului de șablon: împărțim un șir cu o expresie regulată, obținem o secvență de bucăți de conținut și specificatori de cod cu următoarele părți ale acestui cod, apoi este suficient să procesăm pur și simplu matrice rezultată.

Generarea codului executabil

Acum avem un șablon analizat, ceea ce înseamnă că ne putem imagina deja cum să-l transformăm destul de liniar în cod JavaScript executabil. Tot ce ne trebuie este să obținem o funcție, apelare la care vom primi conținut format corect conform șablonului original. Deci, părțile de conținut din șablon trebuie inserate așa cum sunt, valorile lor trebuie introduse în locul variabilelor, iar codul trebuie executat.

Să organizăm acest lucru în următorul mod simplu:

  • vom forma funcția ca un șir potrivit pentru apelarea eval
  • în corpul funcției organizăm atașarea secvențială a bucăților de conținut la o anumită variabilă de rezultat
  • vom lipi codul JS obișnuit așa cum este

Cu toate acestea, va trebui să rezolvăm următoarele probleme:

  • trecerea variabilelor și a funcțiilor de ajutor în corpul funcției șablon executabil
  • organizarea variabilei rezultat astfel încât să nu interfereze cu variabilele utilizate în șablon
Rezolvarea primei probleme

Pentru a rezolva prima problemă, cel mai simplu mod este să folosiți constructul with. Da, exact acesta este subiectul etern al discuțiilor aprinse între dezvoltatori și o bătaie de cap pentru standardizatorii w3c, tocmai asta urăsc atât de mult tot felul de validatori precum JSLINT și compilatori precum Closure Compiler. Ceea ce face acest construct este cel mai ușor considerat ca oferind un obiect ca un strat de vizibilitate pentru variabilele locale, ceea ce înseamnă că puteți accesa proprietățile obiectului după nume, ca și cum ar fi variabile obișnuite, adică fără a prefix numele obiectului cu un punct, deci iată cum arată:

Var obj = ( foo: „Este foo.”, bar: 42 ); cu (obj)( foo += " Și bara este " + bara; )

Când citiți valori, totul este destul de simplu, nu atât de banal când scrieți, mai ales când folosiți imbricat cu constructe, dar în șabloane va trebui să citim în principal valori, așa că folosirea abordării native în loc de a crea o altă cârjă este mai mult decât justificată. Mai exact, creează de fapt două niveluri imbricate de vizibilitate ale variabilelor locale: unul din obiect, celălalt în interiorul cu body, adică atunci când se creează variabile locale acolo, acestea nu ajung în obiectul original.

Rezolvarea celei de-a doua probleme

Tot ce rămâne este să ne dăm seama cum vom asambla părțile de conținut în funcția de randare a șablonului. Primul lucru care vă poate veni în minte este să creați pur și simplu o variabilă locală în corpul funcției cu un nume rar întâlnit și să atașați la ea tot ceea ce este necesar în timpul execuției. Sincer, la început am vrut să fac asta, aceasta este o idee foarte bună dacă reduceți la minimum probabilitatea de a intersecta un nume de variabilă. Cu toate acestea, inutil să spunem, această soluție nu este foarte estetică, ca creatori de artă înaltă, nu avem dreptul de a crea vreo variabilă locală în această funcție imaculată.

Să vedem ce avem, dar în general nu există multe opțiuni: folosiți cumva acest obiect sau obiectul arguments, pe care, din cauza particularităților limbajului, utilizatorul oricum nu ar trebui să le folosească în șabloane și, dacă poate, atunci doar într-un fel special. Luând în considerare toate argumentele pro și contra, am decis să folosesc arguments , o serie de argumente nenumite transmise funcției, într-unul dintre ele și vom pune tot ce este necesar pentru a genera corect conținutul.

Caracteristici de implementare

Acum avem totul pentru a implementa un motor de șabloane complet. După cum am spus, nu va fi pe deplin compatibil cu EJS original, dar va putea folosi șabloanele create pentru acesta. De asemenea, nu va exista nicio implementare a unui încărcător automat de șabloane, pe care trebuie să-l instalați singur, pe baza principiilor cadrului pe care îl utilizați în acest mediu de execuție particular.

Este timpul să mergem direct la cod, să privim totul în părți, începând cu lucrul principal și principal, și anume, obiectul șablon:

// Constructor var EJS = function(src)( if(typeof src == "string")( // Dacă un șablon este trecut this.compile(src); // Compilați-l imediat ) );

EJS.prototype.compile = function(src)( delete this.method; delete this.error; // șterge urmele apelului anterior al compilatorului var p = src.split(this.regexp), // Rezultatul analizei r = , // Rezultatul generează i, o;< p.length; i++){ if(p[i] == " 0) { str += parts[ len ]; } } return str; }

Codul de mai sus este puțin mai mic decât rezultatul final.
Deci, de exemplu, nu am arătat ce să fac cu elementul curent dacă este setat la un punct.
De asemenea, nu am furnizat procesarea filtrului.
În plus, în versiunea finală, am adăugat „pe cont propriu” la procesarea situațiilor în care „elementul curent” sau „valoarea pentru” sunt funcții.

Dar scopul meu a fost să arăt conceptul în sine...

Și rezultatul, așa cum sa menționat deja la începutul articolului, poate fi găsit.
Ultimul exemplu.

Sper sa fie de folos cuiva.
Vă mulțumim pentru atenție!

Numărul de biblioteci JS nu scade în niciun fel; dimpotrivă, crește în fiecare zi. Când ajungem la aplicațiile JS, cea mai buna alegereșabloanele se dovedesc a fi mai bune decât bibliotecile cu drepturi depline, deoarece rezultă o bază de cod mai curată și o experiență mai bună de lucru cu ele.

Nu cu mult timp în urmă am scris că ai putea încerca să-ți scrii propria bibliotecă atunci când va veni momentul. Motoarele de șabloane, pe de altă parte, necesită puțin mai multă pricepere și înțelegere a limbii cu care lucrați, așa că este mai bine să vă bazați pe orice motor de șabloane din lista de mai jos.



O listă a acestora poate fi găsită pe Wikipedia, care face o treabă grozavă de a compara motoarele pentru diferite limbaje de programare web, dar nu se concentrează cu adevărat pe o singură limbă, așa că aș dori să văd câte motoare pot fi listate pentru Javascript .

Dacă dezvoltați în Javascript, veți recunoaște o serie de motoare, dar puteți afla și despre unele noi. Mi-ar plăcea să continuăm această listă împreună și sper să vă facă fericiți.

chibi.js

Chibi vă oferă totul pentru a economisi trafic și timp pentru afișarea unui șablon, o bibliotecă mică și ușoară care vă va ajuta să șablonați mai bine aplicația dvs. Se concentrează mai mult pe CSS în loc să folosească animația. („apa” literală a autorului - traducere)

templated.js

Acesta este cel mai mic motor de șabloane pe care îl veți întâlni, garantat (Nano - traducere). Este construit peste Mustache și este ușor de utilizat și de înțeles. Site-ul are un exemplu demo mare în care puteți rula și testa codul.

ECT

La fel ca șablonul, ECT are, de asemenea, pagini de instalare demonstrative pe site-ul web cu care vă puteți juca și puteți vedea rezultate live. Este construit pentru viteză și pretinde a fi cel mai rapid motor de șabloane JS (construit pe Coffeescript). Compatibil cu Node.js și are o sintaxă clară. Există benchmark-uri și teste unitare pe Github care arată eficiența acestei biblioteci.

Pithy.js

Există un DSL intern pentru generarea HTML în JavaScript. Acest lucru este excelent pentru proiectele front-end mici, dar nu este recomandat pentru pagini HTML grele.

T.js

T.js folosește structură simplă Date Javascript de reprezentat date HTML/xml.

Nunjucks

Creat în Mozilla, Nunjucks este creat pentru cei care au nevoie de performanță și flexibilitate datorită capacității de a extinde biblioteca de pluginuri și funcții a utilizatorului.

Jad

Jade este conceput în primul rând pentru șabloane de pe partea de server în node.js, dar poate rula în multe alte medii. Este realizat doar pentru documente asemănătoare XML (HTML, RSS, ...), așa că nu-l folosiți pentru design text simplu, markdown, CSS și documente similare.

Dust.js

Praful extinde Mustața și oferă calitate superioară performanță comparativ cu alte soluții din această listă. Conține un API foarte simplu și ușor de înțeles.

Motoare de șabloane Javascript Nu am încercat să ofer exemple, deoarece multe dintre linkurile către paginile oficiale conțin demonstrații.

Sper că ai reușit să descoperi noi opțiuni pentru următorul tău proiect. Sunt sigur că există multe alternative care nu sunt menționate, dar cele enumerate au cel mai mult sens.

UPD: au adăugat comentatorii articolului original:

  • „Pure - Instrument de șabloane simplu și ultra-rapid pentru a genera HTML din datele JSON
  • Dust.js este folosit de PayPal și implicit în cadrul Kraken.js.
  • Swig - Un motor de șabloane JavaScript simplu, puternic și extensibil.
UPD2: menționat în comentariile la acest articol:
  • Twig - Implementarea JS a limbajului de șabloane Twig
  • Șabloane JavaScript încorporate EJS pentru nod

Cadrele de șabloane separă aspectul (aspectul paginii) site-urilor web dinamice și aplicațiilor de logica integrată în acestea. Toate cadrele acceptă diferite forme de bucle, logică condiționată și formatare specială. Dar îmi doream un motor care să aibă instrumente avansate de creare a paginilor pentru mai multe scopuri:

  • Pentru partajarea marcare pe pagini;
  • să includă blocuri individuale cu funcționalitate (dacă este necesar);
  • în general, pentru a simplifica procesul de construire a machetelor de pagină moderat complexe cu un minim de efort.
Comparația motoarelor de șabloane

Există o serie de motoare de șabloane populare pentru JavaScript și ne vom uita la unele dintre ele:

E.J.S.

EJS (Embedded JavaScript) este foarte asemănător cu PHP sau JSP, funcții foarte simple - prea simple pentru a fi de orice folos. Îl excludem imediat.

Jad

Cea mai distinctivă caracteristică a lui Jade este că denotă un spațiu fără ghilimele (cum ar fi Coffeescript), care este fie un miracol (dacă crezi în astfel de lucruri), fie un eșec catastrofal dacă ești realist (ca mine). În ceea ce privește caracteristicile sale, acest motor de șablon este la egalitate cu majoritatea celorlalte sisteme, dar stilul său ne face să îl renunțăm aproape imediat.

Mustață/Gudon

Folosește familiarul ((sistem token)) pentru a încorpora logica în șabloanele existente (și nu doar HTML). Oferă un set bun de bucle, logică și management al variabilelor. Nu funcționează foarte bine cu șabloane și blocuri parțiale și cu alte elemente necesare pentru a crea mai ușor structuri de pagină moderat complexe, o cerință cheie chiar și pentru cele mai simple sisteme CMS, pe care aș vrea să-l văd. Să mergem mai departe...

Praf

În prezent popular datorită aplicării sale pe LinkedIn. La prima vedere, foarte asemănător cu Mustache / Ghidon, dar cu o serie de completări funcții utile, cum ar fi blocurile cu nume. Cu toate acestea, procesul de încărcare și randare a șablonului este destul de neplăcut și încă nu acceptă o gamă destul de largă de caracteristici de creare a paginii. Bun, dar nu suficient...

Nunjucks

Un altul ((sistem bazat pe token)), care oferă (ca și alte motoare) logică, buclă și capacitatea de a manipula variabile. Dar, pe lângă aceasta, motorul de șabloane oferă elemente avansate de construire a paginii, cum ar fi moștenirea blocurilor, inclusiv moștenirea aspectului, etichete personalizate și macrocomenzi. Toate acestea sunt cele mai potrivite pentru un CMS, în care paginile sunt o serie de blocuri.

Deci avem un câștigător!

Pentru referință Exemple de randare de bază

Să ne uităm la câteva exemple ale motorului de șablon Nunjucks în acțiune...

Redare dinamică pe Node folosind nunjucks.render

În primul rând, vom analiza un exemplu în care vom folosi Nunjucks pentru a reda o pagină ca răspuns la o solicitare de serviciu web care este procesată de un server Node. Acest scenariu este foarte asemănător cu ceea ce se întâmplă atunci când solicitați o pagină PHP sau ColdAusion sau ASP .NET pe un site web sau aplicație web obișnuită.

Să presupunem că Node este deja instalat.

Instalați Express și Nunjucks:

Npm install express --save npm install nunjucks --save

Să creăm structura noastră de bază app.js:

Var express = require("express"); var nunjucks = require("nunjucks"); var app = expres();

Configurarea Nunjucks:

Var PATH_TO_TEMPLATES = "." ; nunjucks.configure(PATH_TO_TEMPLATES, ( autoescape: true, express: app ) );

Mod simplu:

App.get("/home.html", function(req, res) ( return res.render("index.html") ; ) ); app.listen(3000);

Creați un șablon index.html:

Salutare tuturor

Fugi de la:

Node app.js

și accesați http://localhost:3000. Gata!

Adăugarea datelor dinamice

Să extindem rapid acest exemplu pentru a arăta cum puteți transmite date șablonului în Nunjucks.

Mai întâi, să transmitem câteva date funcției de randare:

App.get("/home.html", function(req, res) ( var data = ( prenume: "Andy", nume: "Neil" ) ; return res.render ("index.html", date) ; ) );

În al doilea rând, vom face referire la aceste date în șablonul nostru index.html:

Bună ziua ((data.name))

Ghici ce se întâmplă când mergem la http://localhost:3000?

Precompilare cu Gulp

Dacă aveți șabloane care nu se bazează pe date dinamice, atunci o alternativă la redarea lor la cerere este să utilizați instrumentul Gulp (sau Grunt dacă preferați) pentru a precompila datele în momentul conectării.

Să presupunem că aveți un site web simplu format din două pagini cu un marcaj similar:

  • src/index.html
  • src/contact-us.html
  • src/_layout.html

Paginile tale ar putea arăta astfel:

(% set title = "Acasă" %} { % extends "_layout.html" %} { % block content %} Добро пожаловать { % endblock %} { % set title = "Contacte" %} { % extends "_layout.html" %} { % block content %} Контакты !}

Sunați-ne la 0800 000 0000 sau trimiteți un e-mail. adresa [email protected].

(%endblock%)

( ( titlu ) ) ( % conținut bloc %) ( % bloc final %)

Instalați pluginul gulp-nunjucks:

Npm instalează gulp-nunjucks --save

Creați un simplu gulpfile.js în folderul rădăcină al proiectului:

Var gulp = require("gulp"); var nunjucks = require("nunjucks"); var COMPILE = ( SRC: "/src/**.html", DEST: "/dist' ) ; gulp.task("render", function() ( return gulp.src(COMPILE.SRC) .pipe(nunjucks( )).pipe(gulp.dest(COMPILE.DEST)) ;

Să începem procesul de randare:

Gulp randare

Paginile HTML rezultate vor arăta astfel:

Acasă Bine ați venit

Nu este acesta cel mai bun site din lume?

Contacte Contacte

Sună-ne la 0800 000 0000 sau trimite-ne un e-mail. adresa [email protected].

Caracteristicile Nunjucks

Documentația completă poate fi vizualizată aici:

În această secțiune vom acoperi câteva dintre elementele de bază pentru a ilustra unele funcții cheie Nunjucks.

Moștenirea șablonului

Moștenirea șabloanelor este cea mai simplă modalitate de a reutiliza șabloanele. Când scrieți un șablon, puteți defini „blocuri” pe care șabloanele copil le pot suprascrie. (Aceasta este ceea ce am făcut mai sus în secțiunea (% conținut blocat%)).

În acest fel, un șablon copil poate moșteni de la un șablon părinte și poate umple mai multe blocuri discrete din acel șablon. De fapt, „lanțul de moștenire” poate fi atât de lung cât doriți să fie. Deci, de exemplu, o secțiune separată de știri pe o pagină web poate moșteni șablonul „știri” (și alte pagini pot avea, de asemenea, propriile șabloane, de exemplu, „bloguri”, „galerie foto”), care, la rândul lor, moștenește șablonul părinte (de bază) care conține secțiuni comune pentru toate paginile site-ului.

Incluziuni

Dezavantajul extensiilor este că includ un șablon în șablonul curent. Acest lucru este util pentru „tragerea” de blocuri de conținut care sunt utilizate în mai multe locuri pe un site sau aplicație.

Import și macrocomenzi

Importul vă permite să încărcați un șablon și să deschideți orice variabile și macrocomenzi (funcții) definite în acesta. Acest lucru poate fi util pentru lucruri precum crearea de funcții pentru a afișa secvențial câmpuri de formular... un pic ca scrierea etichetelor personalizate cu ColdFusion, de exemplu. Pentru claritate, să ne uităm la un șablon numit forms.html, care arată astfel:

( % câmp macro (nume, valoare = "", tip = "text") %) ( % macro macro %) ( % macro etichetă (text) %) ( ( text ) ) ( % macro final %)

Putem importa acest șablon și îl putem folosi pentru a reda rapid o serie de câmpuri de formular:

( % import "forms.html" ca formulare %) ( ( forms.label("Nume utilizator") ) ) ( ( forms.field("utilizator") ) ) ( ( forms.label("Parolă") ) ) ( ( forms.field("pass", tip = "parolă")) )

Acest lucru este util în special atunci când utilizați cadre precum Foundation sau Bootstrap care necesită o cantitate rezonabilă de cadre HTML și CSS - dacă puteți face acest lucru redând într-o macro, atunci codul șablonului va fi mult mai simplu. (În plus, dacă șablonul dvs. face toată redarea prin macrocomenzi, atunci nu este direct legat de un cadru, ceea ce facilitează schimbarea cadrelor și vă permite, de asemenea, să faceți modificări comune tuturor câmpurilor de formular de un anumit tip).

Logica – Dacă / Pentru / În timp ce

dacă verifică starea și este utilizat pentru a afișa selectiv conținut sau pentru a efectua alte operațiuni:

( % dacă variabilă %) Variabila există, da ( % endif %) ( % dacă user.authorised %) ( % extinde „logged-in.html” %) ( % else %) ( % extinde „logged-out.html” " %) (% endif %)

pentru bucle peste matrice:

Var mesaje = ["Trebuie să introduceți o adresă de e-mail", "Parola trebuie să aibă cel puțin 8 caractere"];

( % pentru mesaj în mesaje %) ( ( mesaj ) )
(%endfor%)

Următorii pași

Vă sfătuiesc să studiați documentația oficială Nunjucks pentru o înțelegere mai clară a proprietăților și capabilităților motorului de șablon.
În recenziile viitoare, voi arăta cum puteți combina capacitățile motorului de șablon Nunjucks cu generatorul de site-uri static Wintersmith pentru a vă construi propriul sistem CMS simplu și multilingv.

Recent am avut de-a face cu un proiect foarte simplu care fusese deja finalizat, în care era necesar să le adaug rezultatul unui arbore de categorii și produse. Datele au venit sub formă de json prin solicitări către API. În partea din față, aveam doar jQuery în arsenalul meu și, după ce m-am obișnuit cu minunatul motor de șabloane Angular, nu am vrut să mă întorc din nou la concatenarea șirurilor și, desigur, nu avea sens să conectez un fel de cadru. Aveam nevoie de un motor de șablon minim care să poată repeta bucăți de aspect, cum ar fi directive unghiulare, cu condiții și variabile.

Înarmat cu articole de John Resig și Krasimir Tsonev, m-am apucat de treabă. Am ajuns cu o funcție care ar putea compila un șablon precum:

min

Să începem cu banalul:

Var TemplateEngine = function(tpl, data) ( // template engine code ) var template = "

Buna, ma numesc. Am ani.

"; console.log(TemplateEngine(șablon, (nume: „Ioan”, vârsta: 23)));

Și așa cum ați ghicit, aș dori ca funcția să revină:

Bună, numele meu este John. Am 23 de ani.

Pentru a detecta variabile js în textul aspectului, vom folosi o expresie regulată:

Var re = /]+)?%>/g

Cu ajutorul acestui regex vom putea găsi tot ce se află între etichete . Parametrul /g înseamnă că nu ne interesează doar o potrivire, ci toate.

Există multe moduri de a folosi o expresie regulată în js, vom folosi metoda .exec():

Var re = /]+)?%>/g; var potrivire = re.exec(tpl);

Dacă facem console.log o variabilă de potrivire, vom obține următoarele:

["", "nume", index: 21, intrare: "

Buna, ma numesc. Am ani.

" ]

După cum puteți vedea, matricea noastră conține un singur element, dar trebuie să procesăm totul, așa că vom încheia toată logica într-o buclă while.

Var re = /]+)?%>/g, potrivire; while(match = re.exec(tpl)) ( console.log(match); )

Lansare acest cod vom găsi atât variabile cât şi .

Acum, partea interesantă este că trebuie să înlocuim variabilele găsite cu valorile lor. Cel mai simplu lucru care îmi vine în minte este să faci un simplu .replace(). Dar acest lucru ar funcționa cu obiecte json simple cu un singur nivel de imbricare. În practică, avem de-a face cu obiecte care au imbricare pe mai multe niveluri:

(nume: „John”, profil: (vârsta: 23))

Și tpl.replace(match, data) nu va mai fi o soluție suficientă. Pentru că atunci când scriem , codul va fi înlocuit cu date[„profile.age”] și va fi nedefinit. Deoarece metoda de înlocuire nu ne convine, ar fi foarte tare dacă s-ar putea executa cod js real între etichete.

Șablon Var = "

Buna, ma numesc. Am ani.

";

Vă voi spune mai jos cum să scăpați de asta.

Cum să implementez acest lucru? În articolul lui John Resig, el folosește noua funcție pentru a crea o funcție dintr-un șir.

Var fn = new Function("arg", "console.log(arg + 1);"); fn(2); // iese 3

Pentru înțelegere, acest cod poate fi considerat ca:

Var fn = function(arg) ( console.log(arg + 1); ) fn(2); // iese 3

fn este o funcție reală care execută o altă funcție care este transmisă ca parametru text.

Acesta este exact ceea ce avem nevoie, trebuie să convertim șablonul în formularul:

Reveni"

Bună, numele meu este " + this.name + ". Am " + this.profile.age + " ani.

";

Dar codul nostru va funcționa doar pentru a scoate variabile între textul aspectului. Dar avem nevoie de un motor de șablon cu cicluri și condiții, iar dacă avem un șablon de genul

Returnează „Abilitățile mele:” + for(var index în this.skills) ( + „" + this.skills + "" + )

desigur vom prinde greșeli. Pentru a rezolva această problemă, John împarte șirul în elemente de matrice și la sfârșit le combină într-un șir.

Var r = ; r.push("Abilitățile mele:"); for(var index in this.skills) ( r.push(""); ) return r.join("");

Următorul pas logic va fi să găsim șirurile noastre js și, executându-le, să adăugați rezultatul executării buclelor js sau a altor funcții la partea dorită a șablonului. Pentru a face acest lucru, vom introduce un cursor variabil suplimentar, care va ști în ce parte a șablonului ne aflăm acum și unde să inserăm codul.

Var TemplateEngine = function(tpl, data) ( var re = /]+)?%>/g, cod = "var r=;\n", cursor = 0, potrivire;

var add = function(line) ( cod += "r.push("" + line.replace(/"/g, "\\"") + "");\n"; ) while(match = re. exec(tpl)) ( adaugă (tpl.slice(cursor, potrivire.index)); adăugare (potrivire); cursor = potrivire.index + potrivire.lungime; ) adăugare (tpl.substr(cursor, lungime tpl - cursor) );

cod += "return r.join ("");"; // /g, reExp = /(^()?(var|if|for|else|switch|case|break|(|)|;))(?:(?=\()|(?=)| $)/g, cod = "var r=;\n", cursor = 0, potrivire var add = function(line, js) ( js? (cod += line.match(reExp) ? line + "\n); ": "r.push(" + line + ");\n"): (cod += line != "" ? "r.push("" + line.replace(/"/g, "\\" ") + "");\n" : ""); returnează adăugare); Noul regex ne va ajuta acum să colectăm codul corect pentru șablonul nostru. După cum puteți vedea, sezonul regulat prevede diferite cicluri și condiții. La ieșirea motorului de șablon obținem acum codul de funcționare corect: Var r=; r.push("Abilitățile mele:"); for(var index în this.skills) ( r.push("

"); r.push(aceste.aptitudini); r.push("

"); ) r.push(""); return r.join(""); Și, desigur, toate acestea vor fi compilate cu succes. Acum, pentru a nu transmite șablonul ca o linie, ci pentru a-l introduce convenabil în corp

pagini html

, îl vom pune între etichete:

În acest caz, tipul mime text/html va fi necunoscut de browser și va omite executarea acestuia. Dar putem obține cu ușurință conținutul acestor etichete folosind .innerHTML.

Să adăugăm acum o verificare la începutul motorului nostru de șablon: dacă linia începe cu #, atunci acesta este id-ul șablonului nostru.

Var html = tpl.charAt(0) === "#" ? document.getElementById(tpl.substring(1)).innerHTML: tpl;

Dacă linia nu începe cu #, atunci am trecut imediat șablonul ca șir.

Iar pasul final pentru noi va fi să scăpăm de apelarea asta înainte de fiecare variabilă, iar pentru aceasta vom trece numele variabilelor de domeniu în noul parametru Function și le vom atribui valorile folosind .apply() metodă.

min

Returnează o nouă funcție (nume, code.replace(/[\r\t\n]/g, "")).apply(this,value);

Var scope = [ ( nume: „articolul 1”, preț: „10$”, ora: „30” ), ( nume: „articolul 1”, preț: „10$”, ora: „30” ), ( nume : „articolul 1”, preț: „10$”, oră: „30” ) ]; var template = tpl("#tpl_id",scop);

Acum, conținutul variabilei șablon va fi un aspect compilat pe care îl putem insera în orice parte a codului.

Până la urmă vom obține versiunea finală motorul nostru de șabloane, care poate primi șabloane după id-ul lor și le poate compila în aspect.

Var tpl = functie (str, date) ( var name = , value = ; var html = str.charAt(0) === "#" ? document.getElementById(str.substring(1)).innerHTML: str; if (typeof(data) === „obiect”) ( pentru (var k în date) ( name.push(k); value.push(data[k]); ) ) var re = /]+)?%> /g, reExp = /(^()?(var|if|for|else|switch|case|break|(|)|;))(?:(?=\()|(?=)|$) /g, cod = "var r=;\n", cursor = 0, potrivire var add = function(line, js) ( js? (cod += line.match(reExp) ? line + "\n": "r.push(" + line + ");\n"): (cod += line != "" ? "r.push("" + line.replace(/"/g, "\\"") + "");\n": ""); returnează adăuga; ); while(potrivire = re.exec(html)) (adăugare(html.slice(cursor, potrivire.index))(potrivire, adevărat); cursor = match.index + match.length ) add(html.substr(cursor, html.length - cursor)); cod += "return r.join("");"; (/[\r\t\n]/g, "")).aplica(aceasta,valoare);