30. III
• SEO
• Viteza de incarcare / performanta
http://googlewebmastercentral.blogspot.ro/2014/05/understanding-web-pages-better.html
Aplicatii single-page - Probleme
31. III
Client + Server (aplicatie izomorfica)
Client
(Browser)
Server
(Node JS)
Aplicatie sync
32. III
Meteor is a complete open source
platform for building web and mobile
apps in pure JavaScript.
38. III Structura unei aplicatii
• /server
• /.meteor
• /client (templates, CSS)
• /public (imagini, alte fisiere statice)
• Fisiere comune server & client
39. III
• Inclusions: insereaza un alt template
{{> nav}}
• Expressions
{{pageTitle}}
• Block helpers
#each, #if, #each, #with, #unless
Spacebars
51. III meteor remove insecure
meteor remove autopublish
Server, filtreaza comentarii afisate: server/publications.js
Client, legatura cu baza de date: client/main.js
Numele meu este Alexandra si astazi va voi vorbi despre NodeJS, de la simplu la complex. Va voi prezenta cateva exemple despre cum putem folosi NodeJs pentru a construi un site, apoi un API, iar in final va voi vorbi despre aplicatii izomorfice – si anume despre platforma Meteor.
Cateva lucruri despre NodeJS:
Este o platforma construita pe baza engine-ului V8 al Google.
A fost creat ca un experiment pentru procesare asincrona, pornind de la supozitia ca modelele de tipul “un thread per request” sunt ineficiente si nu scaleaza atat de bine comparativ cu cele bazate pe evenimente si callback-uri.
NodeJS exista de ceva vreme (din 2009 mai exact) si ar fi destul de usor sa uitam ca executia secventiala a codului (ex. PHP sau Python) era inainte o regula.
Dar, sa trecem mai departe si sa vedem cum putem folosi NodeJS pentru a face un site.
Primul meu server de Node folosea modulul http. Neavand pe cine sa intreb – hei, exista cumva o solutie mai buna – am aflat intamplator despre ExpressJS. La ora actuala sunt o multime de framework-uri de Node, unele mai bune decat altele, deci cu siguranta aveti de unde alege.
Eu am ramas la prima optiune, daca pot spune asa, din simplu motiv ca Express este cel mai popular framework de Node (si discutabil si cel mai bine mentinut). El se autointituleaza ca “Fast, unopinionated, minimalist web framework” si vom vedea imediat de ce.
Express este unopinionated pentru ca by default nu se instaleaza cu nici un pachet pentru baza de date sau autentificare.
Are un mecanism simplu de rutare
Suporta mai multe engine-uri de template-uri, by default vine cu Jade. In afara de Jade, Express ofera suport si pentru Handlebars si Hogan.js sau putem instala un alt engine de template-uri daca vrem.
Versiunea 4 a framework-ului nu mai este dependenta de la middleware-ul* Connect, ci foloseste librarii independente. Singurul middleware care este integrat este static.
Question – este cineva care nu stie ce inseamna middleware?
Middleware-ul este o functie apelata de framework inainte de a ajunge la ultimul request handler, cu alte cuvinte o portiune de cod ce este executata intre primirea unui request si afisarea raspunsului.
Voi trece peste partea cu instructiunile de instalare, mai ales ca exista un tutorial chiar pe site-ul lor ce este foarte usor de urmarit. Puteti vedea structura unei aplicatii pe care am generat-o, avand deja toate modulele dependente instalate.
In directorul public sunt incarcate toate fisierele de css, imagini, sau fisierele de javascript pentru partea de client, etc.
In views avem template-urile.
In routes sunt incarcate rutele cu functiile lor corespunzatoare, imi place cum sunt organizate pentru ca face usoara impartirea pe module.
Punctul de plecare al aplicatiei este app.js, unde sunt incarcate toate librariile middleware, rutele si este configurata raportarea erorilor in modurile development si production.
View-ul principal este cel de layout. Acesta este template-ul de baza ce va fi folosit de celelalte view-uri, deci are sens ca aici sa includem fisierele comune pentru toata aplicatia.
Puteti vedea ca am folosit cateva variabile (de ex. titlul paginii, meta keywords, o clasa de css pentru body) ce vor fi setate de catre fiecare pagina in parte.
De asemenea, am adaugat 3 blocuri: extraStyling si extraScripts – pentru fisierele de css ce vor fi incluse doar pe anumite pagini si content – ce reprezinta sectiunea in care va fi incarcat continutul propriu-zis.
Jade are o sintaxa proprie ce mi-a adus aminte de Python. Ca si parere pur personala, prefer engine-urile ce permit cod HTML clasic, dar ceea ce mi-a placut este ca suporta logica la nivel de template si este usor de invatat.
Dar, sa vedem si un caz concret de implementare. Am ales ca exemplu un formular de contact, pentru ca este o functionalitate intalnita pe foarte multe site-uri si e ceva mai greu de facut decat o simpla afisare a unui template. Deci, cum construim un formular?
Am inceput cu partea de view, adica am creat un fisier contact.jade. Aici am precizat ca vrem sa folosim layoutul ca baza, apoi am inclus fisierele suplimentare de css si javascript pentru validari, submit prin Ajax si Captcha (fisiere care sunt incarcate doar pe aceasta pagina).
Mai departe, am scris continutul pentru blocul content. Aici pur si simplu avem codul care afiseaza formularul. Ca si observatie, pentru partea de client se pot folosi aceleasi librarii de jQuery sau orice altceva foloseati si pana acum.
Mai departe, am adaugat o ruta care incarca acest view. Am folosit fisierul index.js unde este incarcata si prima pagina a aplicatiei, dar se poate crea si un fisier separat.
Dupa cum vedeti, tot aici sunt setate variabilele cu titlul paginii si clasa body care sunt folosite in layout.
Mai departe, avem nevoie de un modul de Node care sa transmita mesajul. Eu am instalat nodemailer, dar sunt multe altele pe care le puteti folosi.
De asemenea, am creat un fisier separat numit ajax.js in care voi defini ruta catre care se face submit la formular. Desigur ca aplicatia noastra trebuie sa stie de el, deci l-am inclus si in app.js.
In fisierul ajax.js am definit pur si simplu ruta si am folosit cel mai banal exemplu pentru nodemailer. Am scris mai intai ruta si datele de conectare la contul meu de Gmail, apoi …
… portiunea de cod care transmite mesajul. Observati ca am afisat raspunsul fara sa verific daca mesajul a fost transmis cu succes, in mod normal in production nu am face asa ceva.
Pentru partea a doua a prezentarii vom discuta putin despre cum facem un API cu NodeJS. Ca si baza vom folosi tot o aplicatie generata cu Express (cu aceeasi structura ca in exemplul anterior).
Sunt anumite lucruri pe care nu le-am acoperit in prima parte si anume:
Cum ne conectam cu o baza de date, daca va amintiti ce am spus la inceput, Express nu vine cu un pachet preinstalat pentru asta
Vom vedea cum includem parametri in rute si cum specificam verbele HTTP
- Apoi, vom discuta putin despre middleware si callback-uri folosind functia next()
Pentru baza de date am ales MongoDB, pentru ca este o alegere foarte populara in NodeJS.
Ca driver am folosit Mongoose, pentru ca este cel recomandat chiar de Mongo si permite validari la nivel de model, casting si query-uri complexe.
Desigur ca mai sunt si altele, cum ar fi MongoSkin, Mongolia, etc, dar in principiu fac apx. acelasi lucru.
Asa va arata structura API-ului nostru. Observati ca acolo unde este cazul am inclus parametri (cu :), acestia vor aparea in request.params. De asemenea, am precizat si verbul HTTP pentru fiecare request in parte.
Conectarea la baza de date se face foarte simplu, trebuie doar sa includem libraria in app.js si sa apelam functia connect() din mongoose.
Pentru a putea citi / scrie date trebuie sa creem si un model. Sunt cateva lucruri de care trebuie sa tineti cont la acest pas si anume:
Colectia nu este creeata automat daca nu exista
By default, mongoose foloseste numele colectiei la singular, deci pt acest exemplu, colectia noastra se numeste de fapt “users”.
Daca un camp din colectie nu apare in schema modelului, atunci el nu va fi exportat / scris, e ca si cum ar fi invizibil pentru aplicatie.
De fapt, rutele acestea se transforma in niste simple cai definite in fisierul users.js din routes. Momentan aceste functii nu fac nimic, doar afiseaza un raspuns in format JSON. Pentru afisare am folosit GET, pentru creare POST, …
… pentru modificare PUT si pentru stergere DELETE.
Acum sa discutam putin despre next().
Cel de-al treilea parametru al unei rute (dupa request si response) este functia de callback next() - de fapt ce face este sa apeleze urmatoarea functie middleware din stack. In exemplu acesta am adaugat o functie middleware care este executata pentru toate request-urile (fara a tine cont de tipul acestora) si afiseaza datele primite prin GET, POST sau params (prin url rewriting), apoi apeleaza urmatoarea functie din stack.
In cazul nostru, middleware-ul pe care doar ce l-am vazut va fi apelat inainte functiilor din modulul de users (conteaza ordinea in care rutele sunt definite). Aveti aici un exemplu de cum citim o lista de utilizatori, apoi procesam datele si le afisam in functia de callback.
Functia de adaugare este ceva mai complicata pentru ca am adaugat si validari. In orice tip de aplicatie este o idee foarte proasta sa lucram fara validari, cred ca suntem cu totii de acord.
Mai departe, am compus un obiect cu toate campurile si l-am salvat apeland functia save(). Desigur acesta este un exemplu f simplu, el ar trebui completat si cu alte verificari suplimentare – de ex. sa vedem ca adresa de email nu exista deja si sa encriptam parola inainte de a o salva.
Pentru testare am folosit Postman dupa cum va spuneam, pur si simplu se seteaza campurile pe care vrem sa le transmitem prin POST si mai jos vedem raspunsul returnat de API.
Am ajuns la ultima parte a prezentarii, si anume aplicatii izomorfice. In primul rand, hai sa vedem ce inseamna asta.
In modelul clasic, browserul (clientul) trimite un request catre server, ce ii serveste pagina (codul HTML) si apoi asteapta urmatorul request. Acest model a functionat foarte bine in trecut pentru ca browserele nu erau foarte puternice, iar continutul incarcat era de obicei static.
Odata cu aparitia aplicatiilor single page (printre primele exemple se numara Gmail), browserul a fost incarcat cu tot mai mult cod Javascript. In prezent, avem multe framework-uri MVC Client cum ar fi Angular, Backbone, Ember, etc, care ofera o interfata complexa la nivel de client, dar introduc de asemenea anumite probleme, cum ar fi:
SEO – o aplicatie care ruleaza numai in browser nu poate servi pagini in format HTML. Exista diverse proiecte pentru a suporta acest feature, si recent Google a inceput sa execute codul Javascript (?), dar lucrurile sunt inca la inceput
Deoarece browserul este responsabil pentru randare, afisarea continutul este de obicei mult mai lenta, ceea ce este in contradictie cu regulile de optimizare ce ne spun ca pagina trebuie sa se incarce cat mai repede.
Ca mentenanta, anumite portiuni de cod (cum ar fi validarile) vor fi duplicate atat la nivel de client cat si de server, de obicei in limbaje diferite.
Solutia pentru toate aceste probleme ar fi o aplicatie hibrida, care se poate servi pagini in format HTML, dar sa ne permita in aceleasi timp acea interfata complexa din browser.
NodeJS a facut posibila aparitia acestor platforme hibride – sau izomorfice. Pentru a vedea cateva exemple despre cum functioneaza acest tip de platforma, am ales Meteor.
Iata definitia Meteor – Meteor is a complete open source platform for building web and mobile web apps in pure Javascript.
Ma rog, limbajul folosit este intr-adevar Javascript atat la nivel de client cat si de server, dar n-as spune ca e “pur”. Ceea ce trebuie sa stiti este ca Meteor foloseste NodeJS la baza, dar este totusi o platforma de sine-statatoare, cu un manager separat de pachete si organizare proprie.
Ce vom vedea in continuare sunt urmatoarele puncte:
Cum se poate adauga un pachet pe o aplicatie Meteor
Cum arata structura unei aplicatii
Ce engine de template-uri foloseste
Ce fel de baza de date foloseste
Despre routing nu vom mai avea timp sa discutam, ceea ce as mentiona totusi este ca meteor nu are un sistem de rutare propriu, ci trebuie sa instalati un pachet. Cel mai des intalnit este iron:router.
Dupa cum spuneam, Meteor are propriul manager de pachete si de asemenea are comenzi pentru a cauta, adauga sau vizualiza lista de pachete instalate pe proiect.
Iata un exemplu de cum cautam un pachet care se numeste “wait-on-lib” din linie de comanda. Dupa cum vedeti, este afisat si autorul fiecaruia.
Exista de asemenea un site care se numeste atmospherejs.com si in care se pot vedea nu numai pachetele listate dupa nume …
… ci si rating-ul fiecaruia, numarul de instalari si comanda meteor.
Numele autorului este scris in fata pachetului, singurele pachetele pentru care numele autorului lipseste sunt cele mentinute de echipa meteor.
O aplicatie Meteor are o structura destul de interesanta pentru ca exista cod comun intre client si server.
Cu alte cuvinte:
Fisierele din directorul client sunt incarcate pentru partea de client
Fisierele din directorul server sunt incarcate pentru partea de server
Directorul public contine fisiere cum ar fi imagini, ce nu trebuiesc procesate in nici un fel
Directorul .meteor contine build-ul aplicatiei. De preferat sa nu schimbati nimic acolo .
Ca sistem de template-uri, Meteor foloseste Spacebars care este compatibil cu Handlebars.
La modul f simplu, spacebards poate include un template in alt template, apeleaza helpere pentru a afisa expresii si contine ceva instructiuni de baza.
Pana acum nu am afisat sau adaugat informatii in baza de date.
Meteor vine la pachet cu MongoDB, in cadrul unei aplicatii Meteor exista by default o baza de date Mongo.
Ca sa accesam consola bazei de date trebuie sa folosim comanda “meteor mongo”, ce functioneaza doar atat timp cat aplicatia meteor ruleaza.
La prima vedere, pare ca exista o singura baza de date in cadrul aplicatiei, dar de fapt sunt doua.
Partea de server functioneaza ca un API ce are rolul de a scrie si de a citi informatiile din baza de date reale.
Clientul pe de alta parte cuprinde o implementare proprie Meteor ce se numeste MiniMongo (cu functionalitati similare cu originalul), ce de fapt mentine un subset al bazei de date de pe server.
De ce un subset? Pentru ca daca baza de date este foarte mare, evident ca nu are sens sa o incarcam pe toata in memoria browserului.
Ca exemplu , vom face o lista de comentarii ce este updatata real-time atunci cand un comentariu este adaugat in baza de date.
Mai intai, am definit view-ul principal, ce la randul lui include un template denumit commentsList.
Restul este pur si simplu cod HTML clasic.
Apoi, avem inca 2 template-uri:
Primul este un container pentru lista de comentarii, si cuprinde un bloc each ce incarca template-ul denumit commentItem.
Cel de-al doilea fisier contine template-ul pentru un singur comentariu, cu informatiile acestuia.
Pana acum am definit doar template-urile, deci ca sa afisam informatii trebuie sa adaugam un helper pe template-ul cu lista. Am facut un fisier in care am adaugat un helper ce pur si simplu returneaza un array cu niste informatii.
Un lucru f important, o data ce ati pornit aplicatia Meteor, nu mai este nevoie sa dati refresh in browser. Atunci cand modificati un fisier, pagina isi va da automat refresh.
Iata cum arata lista de comentarii in browser.
Legatura cu baza de date se face foarte simplu, trebuie doar sa cream un singur fisier in care sa definim numele colectiei pe care vrem sa o folosim.
Spre deosebire de exemplul pe care l-am vazut mai devreme cu mongoose, daca aceasta colectie nu exista, ea va fi creata automat.
Dupa ce am facut asta, putem sa inseram un document nou chiar din consola browserului.
De fapt, aceasta va fi adaugat in baza de date client, dar Meteor va avea grija ca modificarile sa se propage in baza de date a serverului.
Dupa cum vedeti, daca pornim consola MongoDB si afisam comentariile, vom vedea ca cel adaugat mai devreme exista in colectia corespunzatoare.
La intrebarea – este secure? Raspunsul evident este ca “NU”.
In production, n-am vrea ca datele sa fie inserate direct din partea de client, ceea ce inseamna ca trebuie sa dezactivam cele doua module cu care aplicatia Meteor vine by default: insecure (ce permite modificarea bazei de date de catre client) si autopublish (ce sincronizeaza bazele de date client si server).
Apoi, in partea de server, vom adaugat o functie de publish ce permite accesul la colectia de comentarii. In exemplu acesta am adaugat un parametru suplimentar in query, vom avea acces in partea de client doar la comentariile care nu sunt marcate ca spam.
De asemenea, in partea de client va trebui sa adaugam un subscription pentru aceeasi colectie.
In final, asa arata helperul nostru modificat. Observati ca in functia respectiva returnam comentariile din baza de date daca exista cel putin unul.
In final, asa arata helperul nostru modificat. Observati ca in functia respectiva returnam comentariile din baza de date daca exista cel putin unul.