Oletetaan, että olen tottunut kehittämään asiakaspuolen sovelluksia jQuery -ohjelmalla, mutta nyt haluaisin alkaa käyttää AngularJS -ohjelmaa. Voitko kuvata tarvittavan paradigman muutoksen? Seuraavassa on muutama kysymys, jotka voivat auttaa sinua vastauksen laatimisessa:
En etsi yksityiskohtaista vertailua jQueryn
ja AngularJS:n
välillä.
JQueryssä suunnittelet sivun ja teet siitä sitten dynaamisen. Tämä johtuu siitä, että jQuery suunniteltiin lisäystä varten, ja se on kasvanut uskomattomasti tästä yksinkertaisesta lähtökohdasta. AngularJS:ssä sinun on kuitenkin aloitettava alusta alkaen arkkitehtuurisi pohjalta. Sen sijaan, että aloittaisit ajattelemalla "Minulla on tämä pala DOM:ää ja haluan saada sen tekemään X", sinun on aloitettava siitä, mitä haluat saavuttaa, sitten suunniteltava sovelluksesi ja lopuksi suunniteltava näkymäsi.
Samoin älä aloita ajatuksella, että jQuery tekee X, Y ja Z, joten lisään AngularJS:n sen päälle malleja ja ohjaimia varten. Tämä on todella houkuttelevaa, kun olet vasta aloittamassa, minkä vuoksi suosittelen aina, että uudet AngularJS-kehittäjät eivät käytä jQueryä lainkaan, ainakaan siihen asti, kunnes he tottuvat tekemään asiat "Angular Way" -periaatteella.
Olen nähnyt monien kehittäjien täällä ja postituslistalla luovan 150 tai 200 rivin koodin jQuery-liitännäisiä, jotka he sitten liimaavat AngularJS:ään kokoelmalla takaisinkutsuja ja $apply
:itä, jotka ovat hämmentäviä ja monimutkaisia; mutta lopulta he saavat sen toimimaan! Ongelmana on se, että useimmissa tapauksissa jQuery-lisäosa voitaisiin kirjoittaa AngularJS:ssä uudelleen murto-osalla koodista, jolloin kaikki muuttuisi yhtäkkiä ymmärrettäväksi ja suoraviivaiseksi.
Lopputulos on tämä: kun etsit ratkaisua, ajattele ensin AngularJS:ssä; jos et keksi ratkaisua, kysy yhteisöltä; jos kaiken tämän jälkeen ei ole helppoa ratkaisua, silloin voit vapaasti tarttua jQueryyn. Mutta älä anna jQueryn muuttua kainalosauvaksi tai et koskaan hallitse AngularJS:ää.
Tiedä ensin, että yhden sivun sovellukset ovat sovelluksia. Ne eivät ole ei verkkosivuja. Meidän on siis ajateltava kuin palvelinpuolen kehittäjä sen lisäksi, että ajattelemme kuin asiakaspuolen kehittäjä. Meidän on mietittävä, miten jaamme sovelluksemme yksittäisiin, laajennettaviin ja testattaviin komponentteihin. Miten* se sitten tehdään? Miten AngularJS:ssä ajatellaan? Seuraavassa on joitakin yleisiä periaatteita jQueryyn verrattuna.
JQueryssä muutamme näkymää ohjelmallisesti. Meillä voisi olla pudotusvalikko, joka on määritelty ul
:ksi näin:
<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>
JQueryssä sovelluslogiikassamme aktivoisimme sen seuraavalla tavalla:
$('.main-menu').dropdownMenu();
Kun vain katsomme näkymää, ei ole heti selvää, että tässä on mitään toiminnallisuutta. Pienissä sovelluksissa se on hyvä. Mutta ei-triviaaleissa sovelluksissa asiat muuttuvat nopeasti sekaviksi ja vaikeasti ylläpidettäviksi.
AngularJS:ssä näkymä on kuitenkin näkymäpohjaisen toiminnallisuuden virallinen tietue. Meidän ul
-ilmoituksemme näyttäisi sen sijaan tältä:
<ul class="main-menu" dropdown-menu>
...
</ul>
Nämä kaksi tekevät saman asian, mutta AngularJS-versiossa kuka tahansa mallia katsova tietää, mitä'on tarkoitus tapahtua. Aina kun uusi kehitystiimin jäsen tulee mukaan, hän voi katsoa tätä ja tietää, että siinä on käytössä direktiivi nimeltä dropdownMenu
; hänen ei tarvitse intuitiivisesti päätellä oikeaa vastausta tai tutkia koodia. Näkymä kertoi meille, mitä piti tapahtua. Paljon siistimpää.
AngularJS:ää vasta-alkajat esittävät usein kysymyksen: Miten löydän kaikki tietynlaiset linkit ja lisään niihin direktiivin. Kehittäjä on aina hämmästynyt, kun vastaamme hänelle: Et tee sitä. Mutta syy siihen, miksi et'tee sitä, on se, että tämä on kuin puoliksi JQuery, puoliksi AngularJS, eikä se ole hyvä. Ongelma tässä on se, että kehittäjä yrittää "tehdä jQueryä" AngularJS:n yhteydessä. Se ei tule koskaan toimimaan hyvin. Näkymä on virallinen tietue. Direktiivien ulkopuolella (tästä lisää jäljempänä) et koskaan, koskaan, ei koskaan muuta DOM:ia. Ja direktiivejä sovelletaan näkymässä, joten tarkoitus on selvä.
Muista: älä suunnittele ja merkitse sitten. Sinun on ensin suunniteltava ja sitten muotoiltava.
Tämä on yksi AngularJS:n ylivoimaisesti mahtavimmista ominaisuuksista, ja se poistaa suuren osan edellisessä kappaleessa mainitsemieni DOM-manipulaatioiden tarpeesta. AngularJS päivittää näkymäsi automaattisesti, jotta sinun ei tarvitse! JQueryssä reagoimme tapahtumiin ja päivitämme sitten sisältöä. Jotain sellaista kuin:
$.ajax({
url: '/myEndpoint.json',
success: function ( data, status ) {
$('ul#log').append('<li>Data Received!</li>');
}
});
Näkymälle, joka näyttää tältä:
<ul class="messages" id="log">
</ul>
Huolien sekoittumisen lisäksi meillä on myös samat ongelmat tarkoituksen merkitsemisessä, jotka mainitsin aiemmin. Mutta vielä tärkeämpää on se, että meidän piti manuaalisesti viitata ja päivittää DOM-solmua. Ja jos haluamme poistaa lokimerkinnän, meidän on koodattava DOMia vastaan myös sitä varten. Miten testaamme logiikkaa DOMin lisäksi? Entä jos haluamme muuttaa esitystapaa? Tämä on hieman sotkuista ja heikkoa. Mutta AngularJS:ssä voimme tehdä tämän:
$http( '/myEndpoint.json' ).then( function ( response ) {
$scope.log.push( { msg: 'Data Received!' } );
});
Ja näkymämme voi näyttää tältä:
<ul class="messages">
<li ng-repeat="entry in log">{{ entry.msg }}</li>
</ul>
Mutta mitä siihen tulee, näkymämme voi näyttää tältä:
<div class="messages">
<div class="alert" ng-repeat="entry in log">
{{ entry.msg }}
</div>
</div>
Ja nyt käytämme järjestämättömän luettelon sijasta Bootstrapin hälytyslaatikoita. Eikä meidän tarvinnut muuttaa ohjauskoodia! Mutta mikä tärkeintä, riippumatta siitä, missä tai miten loki päivitetään, myös näkymä muuttuu. Automaattisesti. Siistiä!
Vaikka en näyttänyt sitä tässä, tietojen sitominen on kaksisuuntaista. Joten lokiviestejä voidaan myös muokata näkymässä vain tekemällä näin: <input ng-model="entry.msg" />
. Ja riemu oli suuri.
JQueryssä DOM on ikään kuin malli. Mutta AngularJS:ssä meillä on erillinen mallikerros, jota voimme hallita haluamallamme tavalla täysin näkymästä riippumatta. Tämä auttaa edellä mainitussa tietojen sitomisessa, ylläpitää huolenaiheiden erottelua ja tuo paljon paremman testattavuuden. Muissa vastauksissa mainittiin tämä seikka, joten jätän asian tähän.
Kaikki edellä mainitut asiat liittyvät tähän yleiseen teemaan: pidä huolenaiheet erillään. Näkymäsi toimii virallisena tallenteena siitä, mitä pitäisi tapahtua (suurimmaksi osaksi); malli edustaa tietojasi; sinulla on palvelukerros uudelleenkäytettävien tehtävien suorittamista varten; teet DOM-manipulaatiota ja täydennät näkymääsi direktiiveillä; ja liimaat kaiken yhteen ohjaimilla. Tämä mainittiin myös muissa vastauksissa, ja ainoa asia, jonka lisäisin, liittyy testattavuuteen, jota käsittelen jäljempänä toisessa osiossa.
Huolien erottamisessa auttaa dependency injection (DI). Jos tulet palvelinpuolen kielistä (Java ja PHP välillä), tämä käsite on sinulle todennäköisesti jo tuttu, mutta jos olet asiakaspuolen kaveri, joka tulee jQuerysta, tämä käsite voi tuntua typerältä, tarpeettomalta tai hipsteriltä. Mutta se ei ole sitä :-)) Laajasta näkökulmasta katsottuna DI tarkoittaa, että voit julistaa komponentteja hyvin vapaasti ja sitten mistä tahansa muusta komponentista vain pyytää sen instanssia ja se myönnetään. Sinun ei tarvitse tietää latausjärjestyksestä, tiedostojen sijainnista tai muusta vastaavasta. Valta ei ehkä heti näy, mutta annan vain yhden (yleisen) esimerkin: testaus. Sanotaan, että sovelluksessamme tarvitsemme palvelun, joka toteuttaa palvelinpuolen tallennuksen REST API:n kautta ja sovelluksen tilasta riippuen myös paikallisen tallennuksen. Kun suoritamme testejä ohjaimillemme, emme halua kommunikoida palvelimen kanssa - testaamme ohjainta. Voimme vain lisätä pilkkopalvelun, jolla on sama nimi kuin alkuperäisellä komponentillamme, ja injektori varmistaa, että ohjaimemme saa automaattisesti väärennetyn palvelun - ohjaimemme ei tiedä eikä sen tarvitsekaan tietää eroa. Testauksesta puheen ollen...
Tämä on oikeastaan osa arkkitehtuuria käsittelevää jaksoa 3, mutta se on niin tärkeä, että laitan sen omaksi ylätason jaksokseen. Kaikista niistä monista jQuery-liitännäisistä, joita olet nähnyt, käyttänyt tai kirjoittanut, kuinka monella niistä oli mukana testisarja? Ei kovin monella, koska jQuery ei ole siihen kovin otollinen. Mutta AngularJS on. JQueryssä ainoa tapa testata on usein luoda komponentti itsenäisesti ja luoda esimerkki- tai esittelysivu, jota vasten testit voivat tehdä DOM-manipulaatiota. Tällöin meidän on siis kehitettävä komponentti erikseen ja silloin integroitava se sovellukseemme. Kuinka hankalaa! Niinpä jQueryn kanssa kehitettäessä valitsemme usein iteratiivisen kehityksen testilähtöisen kehityksen sijasta. Ja kuka meitä voisi syyttää? Mutta koska meillä on huolenaiheiden erottelu, voimme tehdä testivetoista kehitystä iteratiivisesti AngularJS:ssä! Sanotaan esimerkiksi, että haluamme erittäin yksinkertaisen direktiivin, joka osoittaa valikossamme, mikä on nykyinen reittimme. Voimme ilmoittaa haluamamme sovelluksemme näkymässä:
<a href="/hello" when-active>Hello</a>
Okei, nyt voimme kirjoittaa testin olemattomalle when-active
-direktiiville:
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();
}));
Ja kun suoritamme testimme, voimme varmistaa, että se epäonnistuu. Vasta nyt meidän pitäisi luoda direktiivimme:
.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' );
}
});
}
};
});
Testimme läpäisee nyt ja valikkomme toimii pyydetyllä tavalla. Kehityksemme on kummallakin tavalla iteratiivista ja testivetoista. Älyttömän siistiä.
Usein kuulee "tee DOM-manipulaatio vain direktiivissä". Tämä on välttämättömyys. Suhtaudu siihen asianmukaisella kunnioituksella!
Mutta sukelletaanpa hieman syvemmälle...
Jotkin direktiivit vain koristelevat sitä, mikä on jo näkymässä (ajattele ngClass
) ja siksi joskus tekevät DOM-manipulaatiota heti ja ovat sitten periaatteessa valmiita. Mutta jos direktiivi on kuin "widget" ja sillä on malli, sen pitäisi kaikki kunnioittaa huolenaiheiden erottelua. Toisin sanoen mallin tulee pysyä pitkälti riippumattomana sen toteutuksesta linkki- ja ohjainfunktioissa.
AngularJS:n mukana tulee kokonainen joukko työkaluja, jotka tekevät tästä erittäin helppoa: ngClass
:n avulla voimme päivittää luokan dynaamisesti, ngModel
mahdollistaa kaksisuuntaisen datan sitomisen, ngShow
ja ngHide
näyttävät tai piilottavat elementin ohjelmallisesti ja monia muita - myös itse kirjoittamiamme. Toisin sanoen, voimme tehdä kaikenlaista mahtavaa ilman DOM-manipulaatiota. Mitä vähemmän DOM-manipulointia, sitä helpompi direktiivejä on testata, sitä helpompi niitä on tyylitellä, sitä helpompi niitä on muuttaa tulevaisuudessa ja sitä helpommin ne ovat uudelleenkäytettävissä ja levitettävissä.
Näen, että monet AngularJS:ää vasta-alkajat käyttävät direktiivejä paikkoina, joihin heittää kasan jQueryä. Toisin sanoen he ajattelevat, että koska en voi tehdä DOM-manipulaatiota kontrollerissa, otan tuon koodin ja laitan sen direktiiviin". Vaikka se on varmasti paljon parempi, se on usein siltikin väärin.
Ajattele loggeria, jonka ohjelmoimme kappaleessa 3. Vaikka laittaisimme sen direktiiviin, haluamme siltikin tehdä sen "Angular Way" -periaatteella. Se siltikään ei vaadi mitään DOM-manipulaatiota! DOM-manipulaatio on usein tarpeen, mutta se on paljon harvinaisempaa kuin luulet! Ennen kuin teet DOM-manipulaatiota missä tahansa sovelluksessasi, kysy itseltäsi, onko se todella tarpeen. Saattaa olla parempi tapa.
Tässä'on nopea esimerkki, joka osoittaa kuvion, jota näen useimmiten. Haluamme vaihdettavan painikkeen. (Huomautus: tämä esimerkki on hieman keksitty ja hieman laajahko kuvaamaan monimutkaisempia tapauksia, jotka ratkaistaan täsmälleen samalla tavalla.)
.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);
});
}
};
});
Tässä on muutama asia pielessä:
angular.element
ja komponenttimme toimii silti, kun se pudotetaan projektiin, jossa ei ole jQueryä.angular.element
) käyttää aina jQueryä, jos se on ladattu! Joten meidän ei tarvitse käyttää $
- voimme vain käyttää angular.element
.$
:iin - elementti
, joka välitetään link
-funktiolle, olisi jo jQuery-elementti!.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;
};
}
};
});
Jälleen kerran, mallinejuttuja on mallineessa, joten voit (tai käyttäjät) helposti vaihtaa sen sellaiseen, joka täyttää minkä tahansa tarvittavan tyylin, eikä logiikkaan ole koskaan tarvinnut koskea. Uudelleenkäytettävyys - boom!
Ja on vielä kaikki muutkin edut, kuten testaus - se on helppoa! Ei ole väliä, mitä mallissa on, direktiivin sisäiseen API:han ei kosketa koskaan, joten refaktorointi on helppoa. Voit muuttaa mallia niin paljon kuin haluat koskematta direktiiviin. Ja vaikka muuttaisit mitä, testit menevät silti läpi.
w00t!
Jos direktiivit eivät ole vain jQueryn kaltaisia funktioiden kokoelmia, mitä ne sitten ovat? Direktiivit ovat itse asiassa HTML:n laajennuksia. Jos HTML ei tee jotain, mitä tarvitset, kirjoitat direktiivin tekemään sen puolestasi ja käytät sitä aivan kuin se olisi osa HTML:ää.
Toisin sanoen, jos AngularJS ei tee jotakin suoraan laatikosta, mieti, miten tiimi saisi sen sovitettua ngClick
:n, ngClass
:n ja muiden kanssa.
Älä edes käytä jQueryä. Älä edes sisällytä sitä. Se jarruttaa sinua. Ja kun törmäät ongelmaan, jonka luulet osaavasi ratkaista jQueryllä jo nyt, ennen kuin tartut $
:iin, yritä miettiä, miten se voidaan tehdä AngularJS:n puitteissa. Jos et tiedä, kysy! 19 kertaa 20:stä paras tapa tehdä se ei tarvitse jQuerya, ja jos yrität ratkaista sen jQueryn avulla, se aiheuttaa sinulle lisää työtä.
jQueryssä valitsimia käytetään DOM elementtien etsimiseen ja sitten tapahtumakäsittelijöiden sitomiseen/rekisteröimiseen niihin. Kun tapahtuma laukeaa, tämä (imperatiivinen) koodi suoritetaan DOM:n päivittämiseksi/muuttamiseksi.
AngularJS:ssä kannattaa ajatella näkymiä eikä DOM-elementtejä. Näkymät ovat (deklaratiivista) HTML:ää, joka sisältää AngularJS:n ohjeita. Suuntaviivat asettavat tapahtumankäsittelijät kulissien taakse ja antavat meille dynaamisen tietokäsittelyn. Valitsijoita käytetään harvoin, joten ID:iden (ja joidenkin luokkien) tarve vähenee huomattavasti. Näkymät on sidottu malleihin (laajuuksien kautta). Näkymät ovat mallin projektio. Tapahtumat muuttavat malleja (eli tietoja, laajuusominaisuuksia), ja näkymät, jotka projisoivat näitä malleja, päivittyvät "automaattisesti";
AngularJS:ssä ajatellaan malleja, eikä jQueryn valitsemia DOM-elementtejä, jotka sisältävät tietojasi. Ajattele näkymiä näiden mallien projisointeina sen sijaan, että rekisteröisit takaisinkutsuja, joilla manipuloit käyttäjän näkemää.
jQuery käyttää huomaamatonta JavaScriptiä - käyttäytyminen (JavaScript) on erotettu rakenteesta (HTML).
AngularJS käyttää kontrollereita ja direktiivejä (joilla kullakin voi olla oma kontrollerinsa ja/tai kääntämis- ja linkittämisfunktiot) irrottamaan käyttäytymisen näkymästä/rakenteesta (HTML). Angularissa on myös palveluita ja suodattimia, jotka auttavat erottamaan/järjestämään sovelluksen.
Katso myös https://stackoverflow.com/a/14346528/215945
Yksi lähestymistapa AngularJS-sovelluksen suunnitteluun:
Voit tehdä paljon jQueryn avulla tietämättä, miten JavaScriptin prototyyppiperintö toimii. AngularJS-sovelluksia kehittäessäsi vältät joitakin yleisiä sudenkuoppia, jos ymmärrät hyvin JavaScriptin periytymistä. Suositeltava lukemisto: https://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs
Voitteko kuvailla, millainen paradigman muutos on välttämätön?
Imperatiivinen vs. deklaratiivinen
jQueryn avulla kerrot DOM:lle, mitä pitää tapahtua, askel askeleelta. AngularJS:ssä kuvaat, mitä tuloksia haluat, mutta et sitä, miten se tehdään. Lisää tästä täällä. Tutustu myös Mark Rajcokin'n vastaukseen.
Miten arkkitehdin ja suunnittelen asiakaspuolen verkkosovellukset eri tavalla?
AngularJS on kokonainen asiakaspuolen kehys, joka käyttää MVC -mallia (katso niiden graafinen esitys). Se keskittyy suuresti huolenaiheiden erotteluun.
Mikä on suurin ero? Mitä minun pitäisi lopettaa tekemästä/käyttämästä; mitä minun pitäisi alkaa tehdä/käyttää sen sijaan?
jQuery on kirjasto
AngularJS on kaunis asiakaspuolen kehys, joka on erittäin testattavissa ja jossa yhdistyvät monet hienot asiat, kuten MVC, dependency injection, datan sitominen ja paljon muuta.
Se keskittyy huolenaiheiden erotteluun ja testaukseen (yksikkötestaus ja päästä päähän -testaus), mikä helpottaa testivetoista kehitystä.
Paras tapa aloittaa on käydä läpi heidän mahtava opetusohjelmansa. Voit käydä vaiheet läpi parissa tunnissa; jos kuitenkin haluat hallita käsitteet kulissien takana, he tarjoavat lukemattomia viitteitä jatkolukemista varten.
Onko palvelinpuolen näkökohtia/rajoituksia?
Voit käyttää sitä olemassa olevissa sovelluksissa, joissa käytät jo puhdasta jQuerya. Jos kuitenkin haluat hyödyntää AngularJS:n ominaisuuksia täysimääräisesti, voit harkita palvelinpuolen koodaamista RESTful -lähestymistavan avulla.
Näin voit hyödyntää niiden resource factory -ominaisuutta, joka luo palvelinpuolen RESTful-API:n abstraktion ja tekee palvelinpuolen kutsuista (get, save, delete jne.) uskomattoman helppoja.