Tarkime, esu susipažinęs su kliento pusės programų kūrimu naudojant jQuery, bet dabar norėčiau pradėti naudoti AngularJS. Ar galite apibūdinti, koks paradigmos pokytis yra būtinas? Pateikiame keletą klausimų, kurie gali padėti suformuluoti atsakymą:
Aš neieškau išsamaus jQuery
ir AngularJS
palyginimo.
Naudodami "jQuery" sukursite puslapį, o tada jį padarysite dinamišką. Taip yra todėl, kad "jQuery" buvo sukurta plėsti ir neįtikėtinai išaugo iš šios paprastos prielaidos. Tačiau "AngularJS" reikia pradėti nuo pat pradžių, turint omenyje savo architektūrą. Užuot pradėję nuo minties "Turiu šį DOM gabalėlį ir noriu, kad jis atliktų X", turite pradėti nuo to, ką norite pasiekti, tada kurti savo programą ir galiausiai kurti vaizdą.
Taip pat nepradėkite nuo minties, kad "jQuery" atlieka X, Y ir Z funkcijas, todėl prie jų pridėsiu "AngularJS" modeliams ir valdikliams. Tai labai vilioja, kai tik pradedate, todėl naujiems AngularJS kūrėjams visada rekomenduoju iš viso nenaudoti jQuery, bent jau tol, kol jie pripras daryti dalykus "Angular Way".
Mačiau, kaip daugelis kūrėjų čia ir adresatų sąraše kūrė sudėtingus 150 ar 200 eilučių kodo sprendimus su jQuery įskiepiais, kuriuos vėliau įkeldavo į AngularJS su daugybe atgalinių skambučių ir $apply
, kurie yra painūs ir painūs; bet galiausiai jiems pavyko viską padaryti! Problema ta, kad daugeliu atvejų "jQuery" įskiepį galima perrašyti į "AngularJS", panaudojant mažesnę kodo dalį, ir staiga viskas tampa suprantama ir paprasta.
Esmė yra tokia: ieškodami sprendimo pirmiausia "galvokite AngularJS";; jei negalite sugalvoti sprendimo, paklauskite bendruomenės; jei po viso to nėra lengvo sprendimo, tada nedvejodami griebkitės jQuery. Tačiau neleiskite, kad jQuery taptų pagalbine priemone, kitaip niekada neįvaldysite AngularJS.
Pirmiausia žinokite, kad vieno puslapio programos yra programos. Tai ne tinklalapiai. Taigi turime mąstyti kaip serverio pusės programuotojas papildomai prie mąstymo kaip kliento pusės programuotojas. Turime galvoti apie tai, kaip suskirstyti savo programą į atskirus, išplečiamus, testuojamus komponentus. Kaip tai padaryti? Kaip mąstyti "AngularJS"? Štai keletas bendrųjų principų, palyginti su jQuery.
JQuery programoje programiškai keičiame rodinį. Galime turėti išskleidžiamąjį meniu, apibrėžtą kaip ul
, pvz:
<ul class="main-menu">
<li class="active">
<a href="#/home">Home</a>
</li>
<li>
<a href="#/menu1">Menu 1</a>
<ul>
<li><a href="#/sm1">Submenu 1</a></li>
<li><a href="#/sm2">Submenu 2</a></li>
<li><a href="#/sm3">Submenu 3</a></li>
</ul>
</li>
<li>
<a href="#/home">Menu 2</a>
</li>
</ul>
Naudodami jQuery, savo taikomojoje logikoje jį suaktyvintume, pvz:
$('.main-menu').dropdownMenu();
Kai tik pažvelgiame į rodinį, ne iš karto akivaizdu, kad čia yra koks nors funkcionalumas. Mažoms programoms tai tinka. Tačiau netrivialiose programose viskas greitai tampa painu ir sunku prižiūrėti.
Tačiau AngularJS programoje rodinys yra oficialus rodinio funkcionalumo įrašas. Mūsų ul
deklaracija atrodytų taip:
<ul class="main-menu" dropdown-menu>
...
</ul>
Šios dvi deklaracijos daro tą patį, tačiau AngularJS versijoje kiekvienas, žiūrintis į šabloną, žino, kas turi įvykti. Atėjęs naujas kūrėjų komandos narys gali pažvelgti į šį šabloną ir žinoti, kad jame veikia direktyva dropdownMenu
; jam nereikia intuityviai suprasti teisingo atsakymo ar naršyti po kodą. Vaizdas mums pasakė, kas turėjo įvykti. Daug švariau.
Kūrėjai naujokai, pradedantys dirbti su AngularJS, dažnai užduoda tokį klausimą: kaip surasti visas tam tikros rūšies nuorodas ir pridėti prie jų direktyvą. Kūrėjas visada nustemba, kai mes atsakome: nereikia. Tačiau to nedarysite todėl, kad tai yra tarsi pusiau jQuery, pusiau AngularJS, ir nieko gero. Problema yra ta, kad kūrėjas bando "daryti jQuery" AngularJS kontekste. Tai niekada gerai neveiks. Vaizdas yra oficialus įrašas. Išskyrus direktyvas (daugiau apie tai toliau), jūs niekada, niekada, niekada nekeičiate DOM. O direktyvos taikomos pavyzdyje*, todėl ketinimai yra aiškūs.
Atminkite: nekurkite, o paskui žymėkite. Privalote kurti, o paskui projektuoti.
Tai viena nuostabiausių "AngularJS" savybių, dėl kurios nereikia atlikti daug DOM operacijų, kurias minėjau ankstesniame skyriuje. AngularJS automatiškai atnaujins jūsų rodinį, kad jums to nereikėtų daryti! Naudodami jQuery reaguojame į įvykius ir tada atnaujiname turinį. Kažkas panašaus:
$.ajax({
url: '/myEndpoint.json',
success: function ( data, status ) {
$('ul#log').append('<li>Data Received!</li>');
}
});
Jei rodinys atrodo taip:
<ul class="messages" id="log">
</ul>
Be to, kad maišome problemas, susiduriame su tomis pačiomis ketinimų žymėjimo problemomis, kurias minėjau anksčiau. Tačiau dar svarbiau tai, kad turėjome rankiniu būdu pateikti nuorodą į DOM mazgą ir jį atnaujinti. O jei norime ištrinti žurnalo įrašą, taip pat turime koduoti DOM mazgą. Kaip išbandyti logiką be DOM? O ką daryti, jei norime pakeisti pateikimą? Tai truputį netvarkinga ir truputį trapu. Tačiau "AngularJS" galime tai padaryti:
$http( '/myEndpoint.json' ).then( function ( response ) {
$scope.log.push( { msg: 'Data Received!' } );
});
O mūsų vaizdas gali atrodyti taip:
<ul class="messages">
<li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
Bet šiaip mūsų rodinys gali atrodyti taip:
<div class="messages">
<div class="alert" ng-repeat="entry in log">
{{ entry.msg }}
</div>
</div>
Dabar vietoj netvarkingo sąrašo naudojame "Bootstrap" įspėjamuosius langelius. Ir mums niekada nereikėjo keisti valdiklio kodo! Bet dar svarbiau, kad nesvarbu, kur ar kaip žurnalas bus atnaujintas, vaizdas taip pat pasikeis. Automatiškai. Tvarkinga!
Nors čia to neparodžiau, duomenų susiejimas yra dvipusis. Taigi šiuos žurnalo pranešimus taip pat būtų galima redaguoti rodinyje, tiesiog atlikus šį veiksmą: <input ng-model="entry.msg" />
. Ir buvo daug džiaugsmo.
JQuery sistemoje DOM yra tarsi modelis. Tačiau AngularJS turime atskirą modelio sluoksnį, kurį galime tvarkyti kaip tik norime, visiškai nepriklausomai nuo rodinio. Tai padeda pirmiau minėtam duomenų susiejimui, palaiko rūpesčių atskyrimą ir suteikia kur kas daugiau galimybių testuoti. Kiti atsakymai paminėjo šį dalyką, todėl aš jį paliksiu.
Visi pirmiau minėti atsakymai yra susiję su šia pagrindine tema: atskirkite rūpesčius. Jūsų vaizdas veikia kaip oficialus įrašas apie tai, kas turi įvykti (didžiąja dalimi); jūsų modelis atspindi jūsų duomenis; turite paslaugų sluoksnį, kuriame atliekamos daugkartinio naudojimo užduotys; manipuliuojate DOM ir papildote vaizdą direktyvomis; ir visa tai suklijuojate valdikliais. Tai buvo minėta ir kituose atsakymuose, o vienintelis dalykas, kurį norėčiau pridurti, yra susijęs su testavimo galimybėmis, kurias aptarsiu kitame skirsnyje.
Atskirti rūpesčius mums padeda priklausomybių injekcija (DI). Jei esate iš serverio pusės kalbos (nuo Java iki PHP), tikriausiai jau esate susipažinę su šia sąvoka, bet jei esate kliento pusės žmogus, atėjęs iš "jQuery", ši sąvoka gali atrodyti nuo kvailos, nereikalingos iki hipsteriškos. Bet taip nėra. :-) Žvelgiant plačiau, DI reiškia, kad galite labai laisvai deklaruoti komponentus, o tada iš bet kurio kito komponento tiesiog paprašykite jo egzemplioriaus ir jis bus suteiktas. Nereikia žinoti apie įkėlimo tvarką, failų vietas ar pan. Galimybė gali būti ne iš karto matoma, bet pateiksiu tik vieną (įprastą) pavyzdį: testavimą. Tarkime, mūsų taikomojoje programoje mums reikia paslaugos, kuri įgyvendina serverio pusės saugyklą per REST API ir, priklausomai nuo taikomosios programos būsenos, taip pat vietinę saugyklą. Atlikdami valdiklių testus, nenorime bendrauti su serveriu - juk testuojame valdiklį. Galime tiesiog pridėti imitacinę paslaugą tuo pačiu pavadinimu kaip ir mūsų pradinis komponentas, o injektorius užtikrins, kad mūsų valdiklis automatiškai gautų imitacinę paslaugą - mūsų valdiklis nežino ir neturi žinoti skirtumo. Kalbant apie testavimą...
Iš tikrųjų tai yra 3 skyriaus apie architektūrą dalis, bet ji'tokia svarbi, kad ją pateikiu kaip atskirą aukščiausio lygio skyrių. Kiek iš daugelio matytų, naudotų ar parašytų jQuery įskiepių turėjote testų rinkinį? Nelabai daug, nes jQuery tam nelabai tinka. Tačiau AngularJS yra. Dažnai vienintelis būdas testuoti "jQuery" yra sukurti komponentą atskirai su pavyzdiniu / demonstraciniu puslapiu, su kuriuo mūsų testai gali atlikti DOM manipuliacijas. Taigi tada turime kurti komponentą atskirai ir paskui integruoti jį į savo programą. Kaip nepatogu! Todėl kurdami su "jQuery" dažniausiai renkamės iteracinį, o ne bandymais pagrįstą kūrimą. Ir kas galėtų mus kaltinti? Bet kadangi turime rūpesčių atskyrimą, "AngularJS" galime iteratyviai kurti pagal testus! Tarkime, norime sukurti itin paprastą direktyvą, kuri mūsų meniu nurodytų, koks yra dabartinis maršrutas. Tai, ko norime, galime deklaruoti savo programos rodinyje:
<a href="/hello" when-active>Hello</a>
Gerai, dabar galime parašyti testą neegzistuojančiai direktyvai when-active
:
it( 'should add "active" when the route changes', inject(function() {
var elm = $compile( '<a href="/hello" when-active>Hello</a>' )( $scope );
$location.path('/not-matching');
expect( elm.hasClass('active') ).toBeFalsey();
$location.path( '/hello' );
expect( elm.hasClass('active') ).toBeTruthy();
}));
Ir kai paleisime savo testą, galime patvirtinti, kad jis nepavyko. Tik dabar turėtume sukurti savo direktyvą:
.directive( 'whenActive', function ( $location ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
scope.$on( '$routeChangeSuccess', function () {
if ( $location.path() == element.attr( 'href' ) ) {
element.addClass( 'active' );
}
else {
element.removeClass( 'active' );
}
});
}
};
});
Dabar mūsų testas praeina ir mūsų meniu veikia taip, kaip reikalaujama. Mūsų kūrimas yra ir iteracinis ir testais paremtas. Nuostabiai šaunu.
Dažnai išgirsite "manipuliacijas su DOM atlikite tik direktyvoje". Tai yra būtinybė. Elkitės su ja su derama pagarba!
Tačiau pasinerkime šiek tiek giliau...
Kai kurios direktyvos tik papuošia tai, kas jau yra rodinyje (pvz., ngClass
), todėl kartais jos iš karto atlieka DOM manipuliacijas ir iš esmės baigia darbą. Tačiau jei direktyva yra kaip "valdiklis" ir turi šabloną, ji taip pat turėtų laikytis rūpesčių atskyrimo. Tai reiškia, kad šablonas taip pat turėtų likti iš esmės nepriklausomas nuo jo įgyvendinimo nuorodos ir valdiklio funkcijose.
"AngularJS" turi visą rinkinį įrankių, kurie labai palengvina šią užduotį; naudodami ngClass
galime dinamiškai atnaujinti klasę; ngModel
leidžia dvipusį duomenų susiejimą; ngShow
ir ngHide
programiškai parodo arba paslepia elementą; ir daug kitų įrankių, įskaitant tuos, kuriuos rašome patys. Kitaip tariant, galime atlikti daugybę nuostabių veiksmų nebandydami manipuliuoti DOM. Kuo mažiau manipuliacijų su DOM, tuo lengviau testuoti direktyvas, tuo lengviau jas stilizuoti, tuo lengviau jas keisti ateityje, tuo lengviau jas pakartotinai naudoti ir platinti.
Matau, kad daug naujų "AngularJS" kūrėjų naudoja direktyvas kaip vietą, kur galima įgrūsti krūvą "jQuery". Kitaip tariant, jie galvoja: "kadangi negaliu manipuliuoti DOM valdiklyje, paimsiu tą kodą ir įdėsiu jį į direktyvą". Nors tai tikrai yra daug geriau, dažnai tai vis tiek yra klaidinga.
Pagalvokite apie loggerį, kurį programavome 3 skyriuje. Net jei jį įrašysime į direktyvą, vis tiek norėsime tai padaryti "Angular Way". Tai vis tiek nereikalauja jokių DOM manipuliacijų! Būna daugybė atvejų, kai reikia manipuliuoti DOM, bet tai yra daug rečiau, nei manote! Prieš atlikdami DOM manipuliacijas visur* savo taikomojoje programoje, paklauskite savęs, ar tikrai to reikia. Galbūt yra geresnis būdas.
Štai trumpas pavyzdys, rodantis dažniausiai pasitaikantį modelį. Norime perjungiamo mygtuko. (Pastaba: šis pavyzdys yra šiek tiek išgalvotas ir šiek tiek žodingas, kad parodytų sudėtingesnius atvejus, kurie sprendžiami lygiai taip pat).
.directive( 'myDirective', function () {
return {
template: '<a class="btn">Toggle me!</a>',
link: function ( scope, element, attrs ) {
var on = false;
$(element).click( function () {
on = !on;
$(element).toggleClass('active', on);
});
}
};
});
Čia yra keletas dalykų, kurie yra negerai:
angular.element
, ir mūsų komponentas vis tiek veiks, jei jį įdėsime į projektą, kuriame nėra jQuery.angular.element
) visada naudos jQuery, jei ji buvo įkelta! Taigi mums nereikia naudoti $
- galime tiesiog naudoti angular.element
.$
- elementas
, perduodamas link
funkcijai, jau būtų jQuery elementas!.directive( 'myDirective', function () {
return {
scope: true,
template: '<a class="btn" ng-class="{active: on}" ng-click="toggle()">Toggle me!</a>',
link: function ( scope, element, attrs ) {
scope.on = false;
scope.toggle = function () {
scope.on = !scope.on;
};
}
};
});
Vėlgi, šablono elementai yra šablone, todėl jūs (arba jūsų naudotojai) galite juos lengvai pakeisti į atitinkančius bet kokį reikalingą stilių, o logikos niekada nereikėjo liesti. Pakartotinio naudojimo galimybės - bum!
Ir dar yra visi kiti privalumai, pavyzdžiui, testavimas - tai paprasta! Nesvarbu, kas yra šablone, vidinis direktyvos API niekada neliečiamas, todėl refaktorizuoti lengva. Galite keisti šabloną tiek, kiek norite, neliesdami direktyvos. Ir nesvarbu, ką pakeisite, jūsų testai vis tiek praeis.
w00t!
Taigi, jei direktyvos nėra tik į jQuery panašių funkcijų rinkiniai, kas jos yra? Iš tikrųjų direktyvos yra HTML plėtiniai. Jei HTML nedaro kažko, ko jums reikia, parašykite direktyvą, kuri tai padarytų už jus, ir tada naudokite ją taip, tarsi ji būtų HTML dalis.
Kitaip tariant, jei "AngularJS" kažko nedaro iš karto, pagalvokite, kaip komanda galėtų tai padaryti, kad tai tiktų prie ngClick
, ngClass
ir kt.
Net nenaudokite jQuery. Net neįtraukite jo. Ji jus stabdys. O kai susidursite su problema, kurią, jūsų manymu, jau žinote, kaip išspręsti jQuery, prieš griebdamiesi $
, pabandykite pagalvoti, kaip tai padaryti AngularJS ribose. Jei nežinai, paklausk! 19 atvejų iš 20, geriausias būdas tai padaryti nereikalauja jQuery, o bandymas tai išspręsti su jQuery jums atneš daugiau darbo.
"jQuery" selektoriai naudojami DOM elementams surasti ir jiems susieti/užregistruoti įvykių tvarkykles. Kai įvykis suveikia, šis (imperatyvusis) kodas vykdomas, kad būtų atnaujintas / pakeistas DOM.
Naudojant "AngularJS" reikia galvoti apie pranešimus, o ne apie DOM elementus. Vaizdai yra (deklaratyvus) HTML, kuriame yra "AngularJS" direktyvų. Direktyvomis už mus nustatomos įvykių tvarkyklės ir suteikiama galimybė dinamiškai susieti duomenų bazę. Selektoriai naudojami retai, todėl ID (ir kai kurių tipų klasių) poreikis labai sumažėja. Vaizdai yra susieti su modeliais (per taikymo sritis). Peržiūros yra modelio projekcija. Įvykiai keičia modelius (t. y. duomenis, apimties savybes), o į tuos modelius projektuojamos peržiūros atnaujinamos "automatiškai.";
AngularJS programoje galvokite apie modelius, o ne apie jQuery pasirinktus DOM elementus, kuriuose saugomi jūsų duomenys. Apie rodinius galvokite kaip apie šių modelių projekcijas, o ne kaip apie grįžtamųjų skambučių registravimą, kad galėtumėte keisti tai, ką mato naudotojas.
"jQuery" naudoja unobtrusive JavaScript - elgesys (JavaScript) yra atskirtas nuo struktūros (HTML).
AngularJS naudoja kontrolerius ir direktyvas (kiekvienas iš jų gali turėti savo kontrolerį ir (arba) kompiliavimo ir susiejimo funkcijas), kad elgsena būtų atskirta nuo rodinio / struktūros (HTML). Angular taip pat turi paslaugas ir filtrus, padedančius atskirti ir (arba) organizuoti jūsų programą.
Taip pat žr. https://stackoverflow.com/a/14346528/215945
Vienas iš AngularJS programos projektavimo būdų:
Su "jQuery" galite daug ką padaryti nežinodami, kaip veikia "JavaScript" prototipinis paveldėjimas. Kurdami "AngularJS" programas išvengsite kai kurių dažniausiai pasitaikančių spąstų, jei gerai suprasite "JavaScript" paveldėjimą. Rekomenduojama skaityti: https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
Ar galite apibūdinti būtiną paradigmos pokytį?
Imperatyvusis vs deklaratyvusis
Naudodami jQuery žingsnis po žingsnio nurodote DOM, kas turi įvykti. Naudodami AngularJS aprašote, kokių rezultatų norite, bet ne kaip tai padaryti. Daugiau apie tai čia. Be to, susipažinkite su Marko Rajkoko atsakymu.
Kaip kitaip kurti ir projektuoti kliento pusės žiniatinklio programas?
AngularJS yra visa kliento pusės karkasas, kuriame naudojamas MVC modelis (peržiūrėkite jų grafinį atvaizdavimą). Joje daug dėmesio skiriama rūpesčių atskyrimui.
Koks didžiausias skirtumas? Ką turėčiau nustoti daryti / naudoti; ką turėčiau pradėti daryti / naudoti vietoj to?
jQuery yra biblioteka
AngularJS yra graži kliento pusės karkasinė sistema, kurią galima labai gerai testuoti ir kuri apima daugybę įdomių dalykų, pavyzdžiui, MVC, priklausomybių injekciją, duomenų susiejimą ir dar daugiau.
Joje daugiausia dėmesio skiriama rūpesčių atskyrimui ir testavimui (vieneto testavimas ir testavimas nuo galo iki galo), o tai palengvina testais pagrįstą kūrimą.
Geriausia pradėti nuo jų nuostabios mokomosios programos. Per porą valandų galite pereiti visus žingsnius, tačiau, jei norite įsisavinti užkulisines sąvokas, jie pateikia daugybę nuorodų tolesniam skaitymui.
Ar yra kokių nors serverio pusės aplinkybių / apribojimų?
Galite ją naudoti esamose programose, kuriose jau naudojate grynąją "jQuery". Tačiau jei norite visiškai išnaudoti AngularJS funkcijas, galite apsvarstyti galimybę serverio pusę koduoti naudodami RESTful metodą.
Tokiu būdu galėsite pasinaudoti jų išteklių gamykla, kuri sukuria jūsų serverio pusės RESTful API abstrakciją ir neįtikėtinai palengvina serverio pusės skambučius (gauti, išsaugoti, ištrinti ir t. t.).