Introduksjon til Stimulus Framework

Det er mange JavaScript-rammer der ute. Noen ganger begynner jeg til og med å tenke at jeg er den eneste som ennå ikke har opprettet et rammeverk. Noen løsninger, som Angular, er store og komplekse, mens noen, som Backbone (som er mer et bibliotek enn et rammeverk), er ganske enkle og bare gir en håndfull verktøy for å fremskynde utviklingsprosessen.

I dagens artikkel vil jeg gjerne presentere et helt nytt rammeverk som heter Stimulus. Det ble opprettet av et Basecamp-team ledet av David Heinemeier Hansson, en populær utvikler som var far til Ruby on Rails.

Stimulus er et lite rammeverk som aldri var ment å vokse til noe stort. Den har sin egen filosofi og holdning til front-end-utvikling, som noen programmerere kanskje liker eller misliker. Stimulus er ung, men versjon 1 er allerede utgitt, så det bør være trygt å bruke i produksjonen. Jeg har spilt med dette rammeverket litt og likte sin enkelhet og eleganse. Forhåpentligvis vil du også nyte det!

I dette innlegget diskuterer vi grunnleggende om Stimulus mens du lager et enkeltprogram med asynkron datainnlasting, hendelser, statlig utholdenhet og andre vanlige ting.

Kildekoden finnes på GitHub.

Introduksjon til Stimulus

Stimulus ble opprettet av utviklere på Basecamp. I stedet for å lage enkeltsides JavaScript-programmer, bestemte de seg for å velge en majestetisk monolitt drevet av Turbolinks og noen JavaScript. Denne JavaScript-koden utviklet seg til et lite og beskjedent rammeverk som ikke krever at du tilbringer timer og timer å lære alle sine konsepter og hilsener.

Stimulus er for det meste ment å knytte seg til eksisterende DOM-elementer og jobbe med dem på en eller annen måte. Det er imidlertid mulig å dynamisk gjengi innholdet også. Alt i alt er dette rammeverket ganske forskjellig fra andre populære løsninger, for eksempel vedvarer det i HTML, ikke i JavaScript-objekter. Noen utviklere kan finne det ubeleilig, men gi Stimulus en sjanse, da det virkelig kan overraske deg.

Rammen har bare tre hovedkonsepter som du bør huske, som er:

  • Controllers: JS-klasser med noen metoder og tilbakeringinger som knytter seg til DOM. Vedlegget skjer når a data-kontrolleren "magisk" attributt vises på siden. Dokumentasjonen forklarer at dette attributtet er en bro mellom HTML og JavaScript, akkurat som klasser fungerer som broer mellom HTML og CSS. En kontroller kan festes til flere elementer, og ett element kan bli slått av flere kontroller.
  • handlinger: Metoder som skal kalles på spesifikke hendelser. De er definert i spesielle data-handling egenskaper.
  • Targets: Viktige elementer som lett kan nås og manipuleres. De er spesifisert ved hjelp av data-target egenskaper.

Som du kan se, tillater attributene som er oppført ovenfor, at du kan skille innhold fra adferdslogikk på en veldig enkel og naturlig måte. Senere i denne artikkelen vil vi se alle disse begrepene i aksjon og legge merke til hvor lett det er å lese et HTML-dokument og forstå hva som skjer.

Bootstrapping en Stimulus Application

Stimulus kan enkelt installeres som en NPM-pakke eller lastes direkte via manus tag som forklart i dokumentene. Vær også oppmerksom på at denne rammen som standard integreres med Webpack-aktivitetsbehandleren, som støtter goodies som autoloading av kontrolleren. Du er fri til å bruke noe annet byggesystem, men i dette tilfellet vil det bli behov for noe mer arbeid.

Den raskeste måten å komme i gang med Stimulus, er å bruke dette startprosjektet som har Express webserver og Babel allerede tilkoblet. Det avhenger også av Garn, så vær sikker på å installere den. For å klone prosjektet og installere alle dens avhengigheter, kjør:

git klon https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter garn installere

Hvis du foretrekker å ikke installere noe lokalt, kan du remix dette prosjektet på Glitch og gjøre all kodingen rett i nettleseren din.

Great-vi er alle satt og kan fortsette til neste avsnitt!

Noen markering

Anta at vi lager et lite enkelttsidsprogram som viser en liste over ansatte og laster inn informasjon som navn, bilde, stilling, lønn, fødselsdato, etc.

La oss starte med listen over ansatte. Alt merket som vi skal skrive skal plasseres inne i offentlig / index.html fil, som allerede har noen veldig minimal HTML. For nå vil vi hardkode alle våre ansatte på følgende måte:

 

Våre ansatte

  • John Doe
  • Alice Smith
  • Blir brun
  • Ann Gray

Hyggelig! La oss legge til et dash av Stimulus magi.

Opprette en kontroller

Som den offisielle dokumentasjonen forklarer, er hovedformålet med Stimulus å koble til JavaScript-objekter (kalt kontrollere) til DOM-elementene. Kontrollerne vil da bringe siden til livs. Som en konvensjon bør kontrollørens navn ende med en _controller postfix (som bør være veldig kjent for Rails-utviklere).

Det er en katalog for kontrollere som allerede er tilgjengelige, kalt src / kontrollere. På innsiden finner du en  hello_controller.js fil som definerer en tom klasse:

import Controller fra "stimulus" eksport standard klasse utvider Controller  

La oss omdøpe denne filen til employees_controller.js. Vi trenger ikke å spesifikt kreve det fordi kontrollerne lastes automatisk takket være følgende kodelinjer i src / index.js fil:

const application = Application.start () const context = require.context ("./ controllers", true, /\.js$/) application.load (definitionsFromContext (kontekst))

Det neste trinnet er å koble vår kontroller til DOM. For å gjøre dette, sett inn a data-kontrolleren Tilordne og tilordne den en identifikator (som er ansatte i vårt tilfelle):

Det er det! Kontrolleren er nå festet til DOM.

Tilbakekalling av livssyklus

En viktig ting å vite om kontrollører er at de har tre livssyklus tilbakeringinger som blir sparket på bestemte forhold:

  • initial: Denne tilbakeringingen skjer bare en gang, når kontrolleren er instantiated.
  • koble: branner når vi kobler kontrolleren til DOM-elementet. Siden en kontroller kan være koblet til flere elementer på siden, kan denne tilbakeringingen kjøre flere ganger.
  • koble fra: Som du sikkert har gjettet, kjører denne tilbakeringingen når kontrolleren koble fra DOM-elementet.

Ingenting komplisert, ikke sant? La oss dra nytte av initialize () og koble() tilbakeringinger for å sikre at vår kontroller faktisk fungerer:

// src / controllers / employees_controller.js eksport standard klasse utvider Controller initialize () console.log ('Initialized') console.log (this) connect () console.log ('Connected') console.log ( dette)

Neste, start serveren ved å kjøre:

garnstart

Navigere til http: // localhost: 9000. Åpne nettleserens konsoll og sørg for at begge meldingene vises. Det betyr at alt fungerer som forventet!

Legge til hendelser

Det neste kjernestimulus-konseptet er arrangementer. Hendelser brukes til å svare på ulike brukerhandlinger på siden: klikke, sveve, fokusere osv. Stimulus forsøker ikke å gjenoppfinne en sykkel, og hendelsessystemet er basert på generiske JS-hendelser.

For eksempel, la oss knytte en klikkhendelse til våre ansatte. Når denne hendelsen skjer, vil jeg gjerne ringe den som ikke eksisterer ennå velge() metode av employees_controller:

 
  • ansatte # velg "> John Doe
  • ansatte # velg "> Alice Smith
  • ansatte # velg "> Vil Brown
  • ansatte # velg "> Ann Gray

Sannsynligvis kan du forstå hva som foregår her selv.

  • data-handling er det spesielle attributtet som binder en hendelse til elementet og forklarer hvilken handling som skal kalles.
  • klikk, selvfølgelig er navnet på arrangementet.
  • ansatte er identifikatoren til vår kontroller.
  • velge er navnet på metoden vi vil ringe.

Siden klikk er den vanligste hendelsen, det kan utelukkes på en sikker måte:

  • John Doe
  • I dette tilfellet, klikk vil bli brukt implisitt.

    Neste, la oss kode på velge() metode. Jeg vil ikke at standardhandling skal skje (som åpenbart åpner en ny side som er angitt i href attributt), så la oss forhindre det:

    // src / controllers / employees_controller.js // tilbakekallinger her ... velg (e) e.preventDefault () console.log (dette) console.log (e)

    e er det spesielle arrangementet objektet som inneholder full informasjon om den utløste hendelsen. Merk, forresten, det dette returnerer kontrolleren selv, ikke en individuell lenke! For å få tilgang til elementet som fungerer som arrangementets mål, bruk e.target.

    Oppdater siden, klikk på et liste element, og observer resultatet!

    Arbeider med staten

    Nå som vi har bundet en klikkhendelseshåndterer til de ansatte, vil jeg gjerne lagre den valgte personen. Hvorfor? Etter å ha lagret denne informasjonen, kan vi forhindre at den samme medarbeider blir valgt andre gang. Dette vil senere tillate oss å unngå å laste inn samme informasjon flere ganger også.

    Stimulus instruerer oss til å fortsette tilstanden i Data API, som virker ganske rimelig. Først av alt, la oss gi noen vilkårlig ids for hver ansatt ved hjelp av data-ID Egenskap:

     
    • John Doe
    • ansatte # velg "> Alice Smith
    • ansatte # velg "> Vil Brown
    • ansatte # velg "> Ann Gray

    Deretter må vi hente id og fortsette det. Bruk av Data API er veldig vanlig med Stimulus, så en spesiell this.data objektet er gitt for hver kontroller. Med hjelpen kan vi kjøre følgende metoder:

    • this.data.get ( 'name'): Få verdien ved attributtet.
    • this.data.set ('navn', verdi): sett verdien under noen attributt.
    • this.data.has ( 'name'): Sjekk om attributtet eksisterer (returnerer en boolsk verdi).

    Dessverre er disse snarveiene ikke tilgjengelige for målene for klikkhendelsene, så vi må holde fast getAttribute () i deres tilfelle:

     // src / controllers / employees_controller.js velg (e) e.preventDefault () this.data.set ("current-employee", e.target.getAttribute ('data-id'))

    Men vi kan gjøre enda bedre ved å skape en getter og en setter for nåværende ansatt:

     // src / controllers / employees_controller.js få currentEmployee () return this.data.get ("current-employee") sett currentEmployee (id) if (this.currentEmployee! == id) this.data.set ("nåværende-ansatt", id)

    Legg merke til hvordan vi bruker this.currentEmployee getter og sørg for at det oppgitte id ikke er det samme som den allerede lagrede.

    Nå kan du skrive om velge() metode på følgende måte:

     // src / controllers / employees_controller.js velg (e) e.preventDefault () this.currentEmployee = e.target.getAttribute ('data-id')

    Oppdater siden for å sikre at alt fortsatt fungerer. Du vil ikke legge merke til noen visuelle endringer ennå, men med hjelp av Inspector verktøyet vil du legge merke til at ul har data-ansatte-current-ansatt Tilordne med en verdi som endres når du klikker på koblingene. De ansatte En del i attributtets navn er kontrollens identifikator og blir lagt til automatisk.

    La oss nå gå videre og markere den valgte arbeidstakeren.

    Bruke mål

    Når en ansatt er valgt, vil jeg gjerne tilordne det tilsvarende elementet med en .valgt ut klasse. Selvfølgelig kan vi ha løst denne oppgaven ved å bruke noen JS-valgfunksjoner, men Stimulus gir en bedre løsning.

    Møt mål, som lar deg markere ett eller flere viktige elementer på siden. Disse elementene kan da lett nås og manipuleres etter behov. For å opprette et mål, legg til en data-target attributt med verdien av Kontrolleren. TARGET_NAME (som kalles a målbeskrivelse):

     
    • John Doe
    • ansatte # velg "> Alice Smith
    • ansatte # velg "> Vil Brown
    • ansatte # velg "> Ann Gray

    La Stimulus vite om disse nye målene ved å definere en ny statisk verdi:

    // src / controllers / employees_controller.js eksport standard klasse utvider Controller static targets = ["employee"] // ...

    Hvordan får vi tilgang til målene nå? Det er så enkelt å si this.employeeTarget (for å få det første elementet) eller this.employeeTargets (for å få alle elementene):

     // src / controllers / employees_controller.js velg (e) e.preventDefault () this.currentEmployee = e.target.getAttribute ('data-id') console.log (this.employeeTargets) console.log (this.employeeTarget )

    Flott! Hvordan kan disse målene hjelpe oss nå? Vel, vi kan bruke dem til å legge til og fjerne CSS klasser med letthet basert på noen kriterier:

     // src / controllers / employees_controller.js velg (e) e.preventDefault () this.currentEmployee = e.target.getAttribute ('data-id') this.employeeTargets.forEach ((el, i) => el .classList.toggle ("valgt", this.currentEmployee === el.getAttribute ("data-id")))

    Ideen er enkel: vi itererer over en rekke mål og for hvert mål sammenligner det data-ID til den som er lagret under this.currentEmployee. Hvis det samsvarer, blir elementet tilordnet .valgt ut klasse. Ellers er denne klassen fjernet. Du kan også trekke ut hvis (this.currentEmployee! == id) tilstand fra setter og bruk den i valgt ut() metode istedenfor:

     // src / controllers / employees_controller.js velg (e) e.preventDefault () const id = e.target.getAttribute ('data-id') hvis (this.currentEmployee! == id) // <--- this.currentEmployee = id this.employeeTargets.forEach((el, i) => el.classList.toggle ("valgt", id === el.getAttribute ("data-id")))

    Ser bra ut! Til slutt vil vi gi noen veldig enkle styling for .valgt ut klasse inne i offentlig / main.css:

    .valgt font-weight: bold; tekst-dekorasjon: ingen; markør: standard; 

    Oppdater siden igjen, klikk på en person, og sørg for at personen blir merket riktig.

    Laster inn data asynkront

    Vår neste oppgave er å laste inn informasjon om den valgte medarbeider. I en real-world-applikasjon må du sette opp en vertsleverandør, en back-end drevet av noe som Django eller Rails, og et API-endepunkt som reagerer med JSON som inneholder alle nødvendige data. Men vi skal gjøre ting litt enklere og konsentrere seg bare om klientsiden. Lag en ansatte katalog under offentlig mappe. Deretter legger du til fire filer som inneholder data for de enkelte ansatte:

    1.json

    "navn": "John Doe", "kjønn": "mann", "alder": "40", "stilling": "CEO", "lønn": "$ 120.000 / år", "bilde": "https : //burst.shopifycdn.com/photos/couple-in-love-at-sunset_373x.jpg " 

    2.json

    "navn": "Alice Smith", "kjønn": "kvinne", "alder": "32", "stilling": "CTO", "lønn": "$ 100.000 / år", "bilde" : //burst.shopifycdn.com/photos/woman-listening-at-team-meeting_373x.jpg " 

    3.json

    "navn": "Will Brown", "gender": "mann", "alder": "30", "posisjon": "Tech Lead", "lønn": "$ 80.000 / år", "bilde" https://burst.shopifycdn.com/photos/casual-urban-menswear_373x.jpg " 

    4.json

    "navn": "Ann Grey", "kjønn": "kvinne", "alder": "25", "stilling": "Junior Dev", "lønn": "$ 20.000 / år", "bilde" https://burst.shopifycdn.com/photos/woman-using-tablet_373x.jpg " 

    Alle bildene ble tatt fra den frie lagerfotografien av Shopify kalt Burst.

    Våre data er klare og venter på å bli lastet! For å gjøre dette, vil vi kode en egen loadInfoFor () metode:

     // src / controllers / employees_controller.js loadInfoFor (employee_id) hente ('ansatte / $ employee_id .json') .then (response => response.text ()) .then (json => this.displayInfo json))

    Denne metoden aksepterer en ansattes ID og sender en asynkron henteforespørsel til den oppgitte URI. Det er også to løfter: en for å hente kroppen og en annen til å vise den lastede infoen (vi legger til den tilsvarende metoden i et øyeblikk).

    Bruk denne nye metoden inni velge():

     // src / controllers / employees_controller.js velg (e) e.preventDefault () const id = e.target.getAttribute ('data-id') hvis (denne.currentEmployee! == id) this.loadInfoFor ) // ...

    Før koding av displayInfo () metode, vi trenger et element for å faktisk gjengi dataene til. Hvorfor utnytter vi ikke målene enda en gang?

     

    Definer målet:

    // src / controllers / employees_controller.js eksport standard klasse utvider Controller static targets = ["employee", "info"] // ...

    Og nå bruk den til å vise all info:

     // src / controllers / employees_controller.js displayInfo (raw_json) const info = JSON.parse (raw_json) const html = '
    • Navn: $ info.name
    • Kjønn: $ info.gender
    • Alder: $ info.age
    • Stilling: $ info.position
    • Lønn: $ info.salary
    'this.infoTarget.innerHTML = html

    Selvfølgelig er du fri til å bruke en templerende motor som håndtak, men for dette enkle tilfellet vil det trolig være overkill.

    Oppdater siden og velg en av de ansatte. Hans bio og bilde burde lastes nesten umiddelbart, noe som betyr at vår app fungerer som den skal!

    Dynamisk liste over ansatte

    Ved å bruke fremgangsmåten beskrevet ovenfor, kan vi gå enda lenger og laste listen over ansatte i fly i stedet for hardkoding av den.

    Forbered dataene i offentlig / employees.json fil:

    ["id": "1", "navn": "John Doe", "id": "2", "navn": "Alice Smith", "id": "3" ":" Vil brun ", " id ":" 4 "," navn ":" Ann Gray "]

    Nå juster du offentlig / index.html fil ved å fjerne den hardkodede listen og legge til en data-ansatte-url attributt (merk at vi må gi kontrollerens navn, ellers vil Data API ikke fungere):

    Så snart kontrolleren er vedlagt DOM, bør den sende en henteforespørsel om å bygge en liste over ansatte. Det betyr at koble() tilbakeringing er det perfekte stedet å gjøre dette:

     // src / controllers / employees_controller.js connect () this.loadFrom (this.data.get ('url'), dette.displayEmployees)

    Jeg foreslår at vi lager en mer generisk loadFrom () metode som aksepterer en URL for å laste data fra og en tilbakeringing for å faktisk gjengi disse dataene:

     // src / controllers / employees_controller.js loadFrom (url, tilbakeringing) hent (url) .then (response => response.text ()) .then (json => callback.call (dette, JSON.parse ))

    Tweak the velge() metode for å dra nytte av loadFrom ():

     // src / controllers / employees_controller.js velg (e) e.preventDefault () const id = e.target.getAttribute ('data-id') hvis (denne.currentEmployee! == id) this.loadFrom (' ansatte / $ id .json ', this.displayInfo) // <--- this.currentEmployee = id this.employeeTargets.forEach((el, i) => el.classList.toggle ("valgt", id === el.getAttribute ("data-id")))

    displayInfo () kan også forenkles, siden JSON nå blir analysert rett innenfor loadFrom ():

     // src / controllers / employees_controller.js displayInfo (info) const html = '
    • Navn: $ info.name
    • Kjønn: $ info.gender
    • Alder: $ info.age
    • Stilling: $ info.position
    • Lønn: $ info.salary
    'this.infoTarget.innerHTML = html

    Fjerne loadInfoFor () og koden på displayEmployees () metode:

     // src / controllers / employees_controller.js displayEmployees (ansatte) let html = "
      "employees.forEach ((el) => html + = '
    • $ El.name
    • ') html + = "
    "this.element.innerHTML + = html

    Det er det! Vi lager nå dynamisk vår liste over ansatte basert på data returnert av serveren.

    Konklusjon

    I denne artikkelen har vi dekket et beskjedent JavaScript-rammeverk kalt Stimulus. Vi har sett hvordan du lager et nytt program, legger til en kontroller med en rekke tilbakeringinger og handlinger, og introduserer hendelser og handlinger. Vi har også gjort noen asynkron data lasting ved hjelp av hent forespørsler.

    Alt i alt, det er det for det grunnleggende av Stimulus-det forventer virkelig ikke at du skal ha noen banan kunnskap for å lage webapplikasjoner. Selvfølgelig vil rammene trolig ha noen nye funksjoner i fremtiden, men utviklerne planlegger ikke å gjøre det til et stort monster med hundrevis av verktøy. 

    Hvis du vil finne flere eksempler på bruk av Stimulus, kan du også sjekke ut denne lille håndboken. Og hvis du leter etter flere JavaScript-ressurser for å studere eller bruke i arbeidet ditt, sjekk ut hva vi har tilgjengelig på Envato Market. 

    Liker du Stimulus? Vil du være interessert i å prøve å skape en real-world-applikasjon drevet av dette rammeverket? Del dine tanker i kommentarene!

    Som alltid, takker jeg deg for at du bodde hos meg og til neste gang.