"Mąstymas AngularJS" jei turiu jQuery fone?

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ą:

  • Kaip kitaip kurti ir projektuoti kliento pusės žiniatinklio taikomąsias programas? Koks yra didžiausias skirtumas?
  • Ką turėčiau nustoti daryti ir (arba) naudoti; ką turėčiau pradėti daryti ir (arba) naudoti vietoj to?
  • Ar yra kokių nors serverio pusės aspektų ir (arba) apribojimų?

Aš neieškau išsamaus jQuery ir AngularJS palyginimo.

Sprendimas

1. Nekurkite puslapio, o paskui jį keiskite naudodami DOM manipuliacijas

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ą.

2. Nedidinkite "jQuery" su "AngularJS

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.

3. Visada galvokite apie architektūrą

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.

Vaizdas yra "oficialus įrašas";

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.

Duomenų susiejimas

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.

Atskiras modelio sluoksnis

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.

rūpesčių atskyrimas

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.

Priklausomybės injekcija

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ą...

4. Į testus orientuotas kūrimas - visada

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.

5. Konceptualiai direktyvos nėra ne jQuery paketas

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:

  1. Pirma, jQuery niekada nebuvo būtina. Čia nedarėme nieko, kam apskritai reikėjo jQuery!
  2. Antra, net jei savo puslapyje jau turime jQuery, nėra jokios priežasties jį čia naudoti; galime paprasčiausiai naudoti angular.element, ir mūsų komponentas vis tiek veiks, jei jį įdėsime į projektą, kuriame nėra jQuery.
  3. Trečia, net ir darant prielaidą, kad jQuery būtų reikalinga, kad ši direktyva veiktų, jqLite (angular.element) visada naudos jQuery, jei ji buvo įkelta! Taigi mums nereikia naudoti $ - galime tiesiog naudoti angular.element.
  4. Ketvirta, glaudžiai susijusi su trečiąja, yra ta, kad jqLite elementų nereikia įvynioti į $ - elementas, perduodamas link funkcijai, jau būtų jQuery elementas!
  5. Ir penkta, apie kurią minėjome ankstesniuose skyriuose: kodėl mes maišome šablonų dalykus į savo logiką? Šią direktyvą galima perrašyti (net ir labai sudėtingais atvejais!) daug paprasčiau, pvz:
.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.

Apibendrinimas

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.

Komentarai (22)

Imperatyvas → deklaratyvas

"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.

Problemų atskyrimas

"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

Programos projektavimas

Vienas iš AngularJS programos projektavimo būdų:

  1. Pagalvokite apie savo modelius. Sukurkite šių modelių paslaugas arba savo "JavaScript" objektus.
  2. Pagalvokite, kaip norite pateikti savo modelius - savo vaizdus. Sukurkite HTML šablonus kiekvienam vaizdui, naudodami reikiamas direktyvas, kad gautumėte dinaminį duomenų susiejimą.
  3. Prie kiekvieno rodinio prijunkite valdiklį (naudodami ng-view ir maršrutizavimą arba ng-controller). Tegul valdiklis suranda ir gauna tik tuos modelio duomenis, kurie reikalingi vaizdui atlikti savo darbą. Padarykite valdiklius kuo plonesnius.

Prototipinis paveldėjimas

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

Komentarai (7)

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.).

Komentarai (4)