Init.js: Un ghid pentru de ce și cum să folosești JavaScript Full-Stack

Publicat: 2022-03-11

Povestea

Deci, tu și co-fondatorul tău aveți această idee grozavă pentru o afacere, nu?

Ai adăugat funcții în mintea ta.

Frecvent, le ceri clienților potențiali părerile lor și tuturor le place.

Ok, deci oamenii o doresc. Sunt chiar niște bani de făcut. Și singurul motiv pentru care nu o pot avea este pentru că nu l-ați implementat — încă.

Așa că, în sfârșit, te așezi într-o zi și spui: „Hai să o facem!” În curând, încercați să vă dați seama cum să implementați logica de afaceri a aplicației dvs., caracteristica ucigașă care va conduce produsul înainte: aveți o idee despre cum să o faceți și știți că o puteți face.

"Terminat! Funcționează!" tu spui. Dovada ta de concept este un succes! Tot ce a mai rămas este să-l împachetați într-o aplicație web.

„Ok, haideți să creăm site-ul”, spuneți.

Și atunci, realizezi adevărul: trebuie să alegi un limbaj de programare; trebuie să alegi o platformă (modernă); trebuie să alegeți niște cadre (moderne); trebuie să configurați (și să cumpărați) stocarea, bazele de date și furnizorii de găzduire; aveți nevoie de o interfață de administrare; aveți nevoie de un sistem de permisiuni; ai nevoie de un manager de conținut.

Vrei să fii slab, vrei să fii agil. Doriți să utilizați tehnologii care vă vor ajuta să reușiți pe termen scurt și lung. Și nu sunt întotdeauna ușor de ales.

Aveți zeci și zeci de decizii arhitecturale de luat. Și vrei să le faci pe cele potrivite: vrei să folosești tehnologii care să permită o dezvoltare rapidă, iterare constantă, eficiență maximă, viteză, robustețe și multe altele. Vrei să fii slab, vrei să fii agil. Doriți să utilizați tehnologii care vă vor ajuta să reușiți pe termen scurt și lung. Și nu sunt întotdeauna ușor de ales.

„Sunt copleșit”, spui, în timp ce te simți copleșit. Energia ta nu mai este aceeași ca odinioară. Încercați să puneți lucrurile împreună, dar este prea multă muncă.

Dovada ta de concept se ofilește încet și moare.

Propunerea

După ce am abandonat eu însumi tone de idei în acest fel, am decis să găsesc o soluție. Îl numesc proiectul „Init” (sau init.js).

Miezul ideii este de a avea un singur proiect pentru a le porni pe toate, pentru a lăsa dezvoltatorul sau fondatorul tehnic să ia toate aceste decizii esențiale simultan și să primească un șablon de pornire adecvat pe baza acestor decizii. Știu ce vor spune detractorii: „O singură soluție nu se poate aplica pentru fiecare problemă” (uratorii o să urască). Și s-ar putea să aibă dreptate. Dar putem face tot posibilul pentru a crea o soluție aproximativă și cred că Init se apropie destul de mult.

Pentru a atinge cel mai bine acest obiectiv, trebuie să avem în vedere câteva idei cheie. Când am dezvoltat Init, am luat în considerare:

  • Componente

    Componentizarea este o caracteristică cheie a oricărui sistem, deoarece vă permite să reutilizați componente software în diferite proiecte, care este obiectivul principal al Init. Dar componentizarea vine și cu un produs secundar, „înlocuibilitatea”, care va fi cel mai bun aliat al nostru în a ataca mai multe probleme diferite cu „aproape” aceeași soluție.

  • Ușurința dezvoltării

    O problemă, undeva are o soluție cel mai bine scrisă în Brainf*ck. Dar implementarea acelei soluții (în Brainfuck) va fi aproape imposibil de scris, darămite de citit. Vă va costa timp și o cantitate enormă de efort. În general, ar trebui să utilizați limbi și platforme care să facă dezvoltarea mai ușoară, nu mai dificilă pentru dvs. (sau pentru oricine ar putea lucra la el mai târziu).

  • Comunitate

    Indiferent de platforma pe care o alegeți, asigurați-vă că are o comunitate mare și una care vă poate ajuta cu cele mai comune și mai puțin frecvente probleme. Amintiți-vă: jQuery poate să nu fie cea mai rapidă, mai curată sau cea mai elegantă bibliotecă, dar este un câștigător doar datorită comunității sale.

Ținând cont de aceste obiective, vă voi arăta în continuare cum am luat propriile mele decizii în crearea Init.

În esență, Init profită de paradigma „full-stack JavaScript” (unii oameni se referă la aceasta, sau la un subset al acesteia, ca MEAN Stack). Lucrând cu o astfel de stivă, Init este capabil să folosească doar o singură limbă, creând în același timp un mediu incredibil de flexibil și complet pentru dezvoltarea de aplicații web. Pe scurt, Init vă permite să utilizați JavaScript nu numai pentru dezvoltarea clientului și a serverului, ci și pentru construirea, testarea, modelarea și multe altele.

Dar să încetinim o clipă și să ne întrebăm: este într-adevăr o idee bună să folosim JavaScript?

De ce am ales JavaScript

Sunt dezvoltator web din 1998. Atunci am folosit Perl pentru majoritatea dezvoltării noastre pe partea de server, dar chiar de atunci am avut JavaScript pe partea client. Tehnologiile serverelor web s-au schimbat enorm de atunci: am trecut prin val după val de limbi și tehnologii precum PHP, AP, JSP, .NET, Ruby, Python, doar pentru a numi câteva. Dezvoltatorii au început să realizeze că utilizarea a două limbi diferite pentru mediile client și server complica lucrurile. Încercările inițiale de unificare într-o singură limbă au încercat să creeze componente client pe server și să le compila în JavaScript. Acest lucru nu a funcționat așa cum era de așteptat și majoritatea acestor proiecte au eșuat (de exemplu: ASP MVC înlocuind formularele web ASP.NET și, probabil, GWT a fost înlocuit în viitorul apropiat de Polymer). Dar a fost o idee grozavă, în esență: o singură limbă pe client și pe server, permițându-ne să reutilizam componente și resurse (acesta este cuvântul cheie: resurse ).

Răspunsul a fost simplu: pune JavaScript pe server.

JavaScript s-a născut de fapt cu JavaScript Server Side în Netscape Enterprise Server, dar limbajul pur și simplu nu era gata la momentul respectiv. După ani de încercări și erori, a apărut în sfârșit Node.js care nu numai că a pus JavaScript pe server, dar a promovat și ideea de programare neblocante, schimbând pentru totdeauna modul în care scriem un „fread” (I/O) (citește aici pentru mai mult).

Într-o singură propoziție: programarea fără blocare urmărește să pună la o parte sarcinile consumatoare de timp, de obicei prin specificarea a ceea ce ar trebui făcut când aceste sarcini sunt finalizate și permițând procesorului să gestioneze alte solicitări între timp.

Dar acele idei nu erau noi, deci de ce au devenit atât de populare cu Node.js? Programarea simplă, fără blocare, poate fi realizată în mai multe moduri. Poate că cel mai ușor este să folosești apeluri inverse și o buclă de evenimente. În majoritatea limbilor, aceasta nu este o sarcină ușoară: în timp ce „callback-urile” este o caracteristică comună în unele alte limbi, o buclă de evenimente nu este și adesea te confrunți cu biblioteci externe (de exemplu: Python, cu Tornado). Dar în JavaScript, callback-urile sunt încorporate în limbaj, la fel ca și bucla de evenimente și aproape fiecare programator care s-a practicat chiar și în JavaScript este familiarizat cu ele (sau cel puțin le-a folosit, chiar dacă nu prea înțeleg care este evenimentul). bucla este). Dintr-o dată, fiecare startup de pe Pământ ar putea reutiliza dezvoltatorii (adică resursele) atât pe partea client, cât și pe server, rezolvând problema postării unui loc de muncă „Python Guru Needed”.

Dintr-o dată, fiecare startup de pe Pământ ar putea reutiliza dezvoltatorii (adică resursele) atât pe partea client, cât și pe server, rezolvând problema postării unui loc de muncă „Python Guru Needed”.

Deci acum avem o platformă incredibil de rapidă (mulțumită programării neblocante) cu un limbaj de programare care este incredibil de ușor de utilizat (mulțumită JavaScript). Dar este suficient? Va dura? Sunt sigur că JavaScript va ocupa un loc important în viitor. Hai sa-ti spun de ce:

  • Programare functionala

    JavaScript a fost primul limbaj de programare care a adus paradigma funcțională în masă (desigur, Lisp a fost pe primul loc, dar majoritatea programatorilor nu au construit niciodată o aplicație pregătită pentru producție folosind Lisp). Lisp și Self, principalele influențe ale Javascript, sunt pline de idei inovatoare. Aceste idei ne-ar putea elibera mințile pentru a explora noi tehnici, modele și paradigme. Și toate se transferă la JavaScript. Aruncă o privire la monade, numerele Bisericii sau chiar (pentru un exemplu mai practic) funcțiile de colecții ale Underscore.js, care te pot salva linii și linii de cod.

  • Obiecte dinamice și moștenire prototipală

    Programarea orientată pe obiecte fără clase (și fără ierarhii nesfârșite de clase) permite o dezvoltare rapidă (creați obiecte, adăugați metode și utilizați-le), dar, cel mai important, reduce timpul de refactorizare în timpul sarcinilor de întreținere, permițând programatorului să modifice în loc instanțele obiectelor. a claselor. Această viteză și flexibilitate deschide calea pentru o dezvoltare rapidă.

  • JavaScript este Internetul

    JavaScript a fost conceput pentru Internet, a fost aici de la început și nu va dispărea. Toate încercările de a-l distruge au eșuat: vezi, de exemplu, căderea applet-urilor Java, înlocuirea VBScript cu TypeScript de la Microsoft (care se compilează în JavaScript) și dispariția Flash din cauza pieței mobile și a HTML5. Este imposibil să înlocuim Javascript fără a sparge milioane de pagini web, așa că obiectivele noastre de viitor ar trebui să fie îmbunătățirea acestuia. Și nimeni nu este mai bun pentru această meserie decât Comitetul Tehnic 39 de la ECMA.

    Ok, alternative la JavaScript se nasc zilnic, cum ar fi CoffeeScript, TypeScript și milioanele de limbi care se compilează în JavaScript. Aceste alternative ar putea fi utile pentru etapele de dezvoltare (prin hărți sursă), dar nu vor reuși să înlocuiască JavaScript pe termen lung din două motive: comunitățile lor nu vor fi niciodată mai mari și cele mai bune caracteristici ale lor vor fi adoptate de scriptul ECMA (adică, JavaScript ). JavaScript nu este un limbaj de asamblare: este un limbaj de programare de nivel înalt cu cod sursă pe care îl puteți înțelege, așa că ar trebui să îl înțelegeți.

Acum, datorită proiectului Esprima, poți crea propriile instrumente pentru a te juca cu codul sursă, modificându-l, schimbându-i stilul, adăugând comentarii, instrumentare și tot felul de lucruri pe care le poți imagina jucând cu Arborele de sintaxă abstractă al programului tău. ca și cum ai lucra cu un arbore DOM.

JavaScript de la capăt la capăt: Node.js și MongoDB

Deci, acestea sunt motivele pentru a utiliza JavaScript. Acum, voi folosi JavaScript ca motiv pentru a folosi Node.js și MongoDB.

  • Node.js

    Node.js este o platformă pentru construirea de aplicații de rețea rapide și scalabile - cam așa spune site-ul Node.js. Dar Node.js este mai mult decât atât: este mediul de rulare preferat pentru orice aplicație JavaScript cu acces I/O. Chiar dacă nu intenționați să scrieți aplicația de server principal cu Node.js, puteți utiliza instrumente construite pe Node.js pentru a vă îmbunătăți procesul de dezvoltare. De exemplu: Mocha.js pentru testarea unitară, Grunt.js pentru sarcini de compilare automate sau chiar Paranteze pentru editarea codului text integral.

    Deci, dacă intenționați să scrieți aplicații JavaScript pentru server sau client, ar trebui să aruncați o privire la câteva exemple Node.js, deoarece veți avea nevoie și de a le folosi zilnic. Există câteva alternative interesante, dar niciuna dintre ele nu este chiar la 10% din comunitatea Node.js.

  • MongoDB

    MongoDB este o bază de date bazată pe documente NoSQL care utilizează JavaScript ca limbaj de interogare, permițându-mi să completez platforma JavaScript end-to-end. Dar acesta nu este nici măcar motivul principal pentru a alege această bază de date.

    MongoDB este o bază de date fără schemă, care vă permite să vă persistați obiectele într-un mod flexibil și, astfel, să vă adaptați mai rapid la schimbările de cerințe. În plus, este foarte scalabil și bazat pe hărți, ceea ce îl face potrivit pentru aplicațiile de date mari. MongoDB este atât de flexibil încât poate fi folosit ca bază de date de documente fără schemă, un depozit de date relațional (deși nu are tranzacții) sau chiar ca un magazin cheie-valoare pentru stocarea în cache a răspunsurilor.

Componentizarea serverului cu Express.js

Componentizarea pe server nu este niciodată ușoară. Dar cu Express.js (și Connect.js) a apărut ideea de „middleware”. În opinia mea, middleware-ul este cel mai bun mod de a defini componentele pe server. Dacă doriți să-l comparați cu un model cunoscut, este destul de aproape de țevi și filtre.

Ideea de bază este că componenta dvs. face parte dintr-o conductă. Conducta procesează o cerere (input) și generează un răspuns (ieșire), dar componenta dvs. nu este responsabilă pentru întregul răspuns. În schimb, modifică doar ceea ce are nevoie și apoi delegă la următoarea parte a conductei. Când ultima bucată din conductă se termină de procesare, răspunsul este trimis înapoi clientului.

Ne referim la aceste „bucăți din conductă” ca „middleware”. În mod clar, putem crea două tipuri de middleware:

  • Intermediari : cei care procesează cererea și răspunsul, dar nu sunt pe deplin responsabili pentru răspunsul în sine, așa că deleg următorul middleware.

  • Finale : cei cu responsabilitate totală asupra răspunsului final. Ei procesează și modifică cererea și răspunsul, dar nu trebuie să delege următorului middleware. În practică, se recomandă să delegați oricum unui middleware următor pentru a permite flexibilitate arhitecturală (adică, adăugarea mai multor middleware mai târziu), chiar dacă acel middleware nu există (caz în care răspunsul va merge direct către client).

Ca exemplu concret, luați în considerare o componentă „manager de utilizatori” pe server. În ceea ce privește middleware-ul, am avea atât finale, cât și intermediare. Pentru finala noastră, am avea caracteristici precum crearea unui utilizator și listarea utilizatorilor. Dar înainte de a putea efectua aceste acțiuni, avem nevoie de intermediarii noștri pentru autentificare (deoarece nu dorim să vină solicitări neautentificate și să creeze utilizatori). Odată ce am creat acești intermediari de autentificare, le putem conecta oriunde dorim să transformăm o caracteristică neautentificată anterior într-o caracteristică autentificată.

Aplicații cu o singură pagină

Proiectul Init se concentrează pe crearea de aplicații cu o singură pagină (SPA). Majoritatea dezvoltatorilor web au fost tentați de mai multe ori să-și încerce mâna la SPA-uri. Am construit mai multe (în mare parte proprietare) și pot spune cu încredere că acestea sunt pur și simplu viitorul aplicațiilor web. Ați comparat vreodată un SPA cu o aplicație web obișnuită pe o conexiune mobilă? Diferența de răspuns este de ordinul zecilor de secunde.

Ați comparat vreodată un SPA cu o aplicație web obișnuită pe o conexiune mobilă? Diferența de răspuns este de ordinul zecilor de secunde.

SPA-urile sunt viitorul web-deci de ce ați construi produsul într-o formă moștenită? Un argument comun pe care îl aud este că oamenii sunt îngrijorați de SEO. Dar dacă gestionați lucrurile corect, aceasta nu ar trebui să fie o problemă: Google însuși are un tutorial foarte bun despre cum să faceți acest lucru și există câteva comentarii bune și aici.

MV* pe partea clientului cu Backbone.js, Marionette.js și Twitter Bootstrap

S-au spus multe despre cadrele MVC* pentru SPA-uri. Este o alegere grea, dar aș spune că primele trei sunt Backbone.js, Ember.js și Angular.js.

Toate trei sunt foarte bine considerate. Dar care este cel mai bun pentru tine?

Din păcate, trebuie să recunosc că am o experiență foarte limitată cu Angular.js, așa că o voi lăsa din această discuție (pentru mai multe despre asta, consultați tutorialul Angular.js). Acum, Ember.js și Backbone.js reprezintă două moduri diferite de a ataca aceeași problemă.

Backbone.js este minimal, simplist și vă oferă suficient pentru a crea un SPA simplu. Ember.js, pe de altă parte, este un cadru complet și profesional pentru crearea SPA-urilor. Are mai multe clopote și fluiere, dar și o curbă de învățare mai mare.

În funcție de dimensiunea aplicației dvs., decizia poate fi la fel de ușoară ca să vă uitați la raportul caracteristiciUsed/featuresAvailable, care vă va oferi un indiciu important.

În cazul Init, am vrut să acopăr majoritatea scenariilor, așa că am ales Backbone.js pentru crearea SPA ușoară, cu Backbone.Marionette.View pentru componentizare. În acest fel, fiecare componentă este o aplicație simplă, iar aplicația finală poate fi atât de complexă pe cât ne dorim să fie.

De asemenea, stilul este o provocare, dar ne putem baza din nou pe cadre care să ne salveze. Pentru CSS, nu există nimic mai bun decât Twitter Bootstrap, care oferă un set complet de stiluri care sunt atât gata de utilizat din cutie, cât și ușor de personalizat.

Bootstrap a fost creat folosind limbajul LESS și este open source, așa că îl putem modifica dacă este necesar. Vine cu o mulțime de controale UX care sunt bine documentate pe site-ul Bootstrap. În plus, există un model de personalizare care vă permite să vă creați propriul model. Este cu siguranță omul pentru job.

Cele mai bune practici: Grunt.js, Mocha.js, Chai.js, RequireJS și CoverJS

În cele din urmă, ar trebui să definim unele dintre cele mai bune practici ale noastre și să vedem cum Init vă poate ajuta să le implementați și să le mențineți. Soluția noastră este centrată pe mai multe instrumente, care se bazează pe Node.js în sine.

  • Mocha.js și Chai.js :

    Aceste instrumente vă permit să vă îmbunătățiți procesul de dezvoltare prin aplicarea TDD sau BDD, oferind infrastructura pentru a vă organiza testele unitare și un runner care să le ruleze automat.

    Există mii de cadre de testare unitară pentru JavaScript. Deci, de ce să folosiți Mocha.js? Răspunsul scurt: este flexibil și complet.

    Răspunsul lung: are două caracteristici importante (interfețe, reporteri) și o absență semnificativă (aserțiuni). Lasă-mă să explic.

    • Interfețe : poate ești obișnuit cu conceptele TDD de suite și teste unitare, sau poate preferi ideile BDD de specificații de comportament cu „descrie” și „ar trebui”. Mocha.js vă permite să utilizați ambele abordări.

    • Reporteri : rularea testului va genera rapoarte ale rezultatelor și puteți formata aceste rezultate folosind diferiți reporteri. De exemplu, dacă trebuie să alimentați un server de integrare continuă, puteți găsi un reporter care să facă exact asta.

    • Lipsa unei biblioteci de aserțiuni : departe de a fi o problemă, Mocha.js a fost conceput pentru a vă permite să utilizați biblioteca de aserțiuni aleasă de dvs., oferindu-vă și mai multă flexibilitate. Există o mulțime de opțiuni, dar aici intervine Chai.js.

    Chai.js este o bibliotecă flexibilă de afirmații care vă permite să utilizați oricare dintre cele trei stiluri majore de afirmare:

    • Assert : Stilul clasic de afirmare din vechea școală TDD. De exemplu:

       assert.equal(variable, ”value”);
    • Așteptați : stilul de afirmare înlănțuit, cel mai frecvent utilizat în BDD. De exemplu:

       expect(variable).to.equal(“value”);
    • Should : Folosit și în BDD, dar prefer Expect, deoarece Should sună repetitiv cu specificația de comportament „it („ar trebui să facă ceva..”)”. De exemplu:

       variable.should.equal(“value”);

    Chai.js se combină perfect cu Mocha.js. Folosind doar aceste două biblioteci, vă puteți scrie testele în TDD, BDD sau în orice stil imaginabil.

  • Grunt.js :

    Grunt.js vă permite să automatizați sarcinile de construire, orice, de la simplu copy-paste și concatenarea fișierelor, până la pre-compilare șabloane, compilare limbaj de stil (adică SASS și LESS), testare unitară (cu mocha.js), lining și minificarea codului (de exemplu, cu UglifyJS sau Closure Compiler). Puteți să adăugați propria sarcină automată la Grunt sau să căutați în registrul Grunt, unde sunt disponibile sute și sute de plugin-uri (din nou, folosirea instrumentelor cu comunități grozave în spatele lor dă roade). Grunt vă poate monitoriza și fișierele și declanșa acțiuni atunci când sunt modificate.

  • Necesită JS :

    RequireJS poate suna ca doar un alt mod de a încărca module cu AMD, dar vă pot asigura că este mult mai mult decât atât. Pentru a înțelege de ce, trebuie mai întâi să menționăm ideea de spațiere a numelor modulelor (de ex., demo.views.hello), care evită poluarea spațiului de nume global prin împachetarea fiecărui modul în propriul spațiu de nume. Problema este că aceste module nu sunt reutilizabile: dacă modificați spațiul de nume al unei „instanțe”, modificați spațiul de nume al tuturor „instanțelor”. Spre deosebire de asta, RequireJS vă permite să definiți module reutilizabile chiar de la început. (În plus, vă va ajuta să adoptați Dependency Injection pentru a evita ca modulele dvs. să acceseze variabile globale.)

  • CoverJS :

    Acoperirea codului este o măsură pentru evaluarea testării dvs. După cum sugerează și numele, vă spune cât de mult din codul dvs. este acoperit de suita dvs. actuală de teste. CoverJS măsoară acoperirea codului testelor tale prin instrumentarea instrucțiunilor (în loc de linii de cod precum JSCoverage) în codul tău și generând o versiune instrumentată a codului tău. De asemenea, poate genera rapoarte pentru a vă alimenta serverul de integrare continuă.

Utilizarea ramurilor pentru a comuta funcții

Când am început Init, aveam nevoie de o modalitate prin care utilizatorii să activeze și să dezactiveze diverse funcții pe care și le-ar putea dori în proiectul lor. Am decis să adopt o abordare radicală a sistemului de ramuri al lui git pentru a implementa această funcționalitate.

În esență, fiecare ramură reprezintă o caracteristică sau o funcționalitate pe care un utilizator ar putea dori să o includă. Dacă începeți un proiect de la zero, începeți cu ramura minimă de care aveți nevoie, apoi adăugați alte tehnologii prin fuzionarea cu ramurile dorite. De exemplu, să presupunem că doriți să începeți proiectul cu Backbone.js și Marionette.js. Ei bine, puteți începe cu ramura Backbone.js și o puteți îmbina cu ramura Marionette, continuând mai departe pentru fiecare bit de funcționalitate pe care doriți să îl adăugați.

init.js și Javascript

Deocamdată, această idee de îmbinare pentru a adăuga funcționalitate poate fi folosită doar pentru șabloanele de tehnologie (de exemplu, Backbone, Node, Express). Dar, în viitor, veți putea comuta între implementările back-end (de exemplu, de la MongoDB la Postgres) și cele ale clientului.

Începeți un proiect cu Init și implementați în Heroku astăzi

Nu a existat niciodată o modalitate mai ușoară de a începe un proiect. Doar mergeți la depozitul GitHub, verificați dacă există ramurile cu cele mai recente comitări (în acest moment este usermanager, deși acest lucru s-ar putea schimba în viitor) și apoi:

  1. Creați directorul pentru proiectul dvs. (sau utilizați unul existent).
  2. Creați-vă depozitul cu „git init” (sau utilizați depozitul existent).
  3. Adăugați o telecomandă cu init

     git remote add init git://github.com/picanteverde/init.git
  4. Ia ramura pe care o vrei

     git pull init usermanager
  5. Obțineți fișierul de proces Heroku

     git pull init heroku-webprocess
  6. Cu Heroku Toolbelt instalată, creați o aplicație Heroku

     heroku create
  7. Împingeți-vă ramura principală către Heroku

     git push heroku master
  8. Vizitați aplicația dvs., funcțională pe Heroku!

Acum puteți începe să vă dezvoltați caracteristica ucigașă cu doar câteva linii de cod. Nu numai asta, dar vei dezvolta cu cele mai noi și mai eficiente tehnologii într-o suită de dezvoltare cât se poate de automatizată.

Sper că poți folosi Init pentru a demara următoarea ta mare idee. Nu uitați să verificați depozitul Init pentru noi remedieri și caracteristici — dezvoltarea sa este foarte activă și aștept cu nerăbdare să aud feedbackul dumneavoastră.