Introductie van het Stimulus Framework

Er zijn veel JavaScript-frameworks die er zijn. Soms begin ik zelfs te denken dat ik de enige ben die nog geen raamwerk heeft gemaakt. Sommige oplossingen, zoals Angular, zijn groot en complex, terwijl sommige, zoals Backbone (dat meer een bibliotheek is dan een raamwerk), vrij eenvoudig zijn en slechts een handvol tools bieden om het ontwikkelingsproces te versnellen..

In het artikel van vandaag zou ik u een geheel nieuw kader willen voorstellen, genaamd Stimulus. Het werd gemaakt door een Basecamp-team onder leiding van David Heinemeier Hansson, een populaire ontwikkelaar die de vader was van Ruby on Rails.

Stimulus is een klein kader dat nooit bedoeld was om iets groots te worden. Het heeft zijn eigen filosofie en houding ten opzichte van de front-end ontwikkeling, die sommige programmeurs wel of niet leuk vinden. Stimulus is jong, maar versie 1 is al vrijgegeven, dus het moet veilig zijn om te gebruiken in de productie. Ik heb redelijk met dit kader gespeeld en vond de eenvoud en elegantie ervan heel goed. Hopelijk geniet je er ook van!

In deze post bespreken we de basisprincipes van Stimulus bij het maken van een toepassing van één pagina met asynchrone gegevensverzameling, gebeurtenissen, statuspersistentie en andere veelvoorkomende dingen.

De broncode is te vinden op GitHub.

Introductie van Stimulus

Stimulus is gemaakt door ontwikkelaars bij Basecamp. In plaats van JavaScript-toepassingen met één pagina te maken, hebben ze besloten om een ​​majestueuze monoliet te kiezen die mogelijk wordt gemaakt door Turbolinks en JavaScript. Deze JavaScript-code is geëvolueerd naar een klein en bescheiden raamwerk waarvoor u niet uren en uren hoeft te besteden aan het leren van al zijn concepten en voorbehouden.

Stimulus is meestal bedoeld om zichzelf te hechten aan bestaande DOM-elementen en op een of andere manier ermee te werken. Het is echter ook mogelijk om de inhoud dynamisch weer te geven. Al met al is dit raamwerk nogal verschillend van andere populaire oplossingen, omdat het bijvoorbeeld status in HTML blijft behouden, niet in JavaScript-objecten. Sommige ontwikkelaars vinden het misschien ongemakkelijk, maar geven Stimulus wel een kans, want het kan je echt verbazen.

Het raamwerk heeft slechts drie hoofdbegrippen die u moet onthouden, namelijk:

  • Controllers: JS-klassen met enkele methoden en callbacks die zich aan de DOM koppelen. De bijlage gebeurt wanneer a data-controller Het attribuut "magic" verschijnt op de pagina. De documentatie legt uit dat dit kenmerk een brug is tussen HTML en JavaScript, net zoals klassen dienen als bruggen tussen HTML en CSS. Eén controller kan aan meerdere elementen worden gekoppeld en één element kan worden opgestart door meerdere controllers.
  • acties: methoden om te worden aangeroepen voor specifieke evenementen. Ze zijn gedefinieerd in het speciaal data-actie attributen.
  • targets: belangrijke elementen die gemakkelijk kunnen worden benaderd en gemanipuleerd. Ze worden gespecificeerd met behulp van data-target attributen.

Zoals u ziet, kunt u met de hierboven genoemde kenmerken op een eenvoudige en natuurlijke manier inhoud scheiden van gedragslogica. Verderop in dit artikel zullen we al deze concepten in actie zien en opmerken hoe gemakkelijk het is om een ​​HTML-document te lezen en te begrijpen wat er aan de hand is.

Bootstrappen met een Stimulatietoepassing

Stimulus kan eenvoudig worden geïnstalleerd als een NPM-pakket of direct worden geladen via de script tag zoals uitgelegd in de documenten. Merk ook op dat dit framework standaard integreert met de Webpack-activabeheerder, die goodies zoals controller autoloading ondersteunt. Je bent vrij om elk ander build-systeem te gebruiken, maar in dit geval zal nog wat meer werk nodig zijn.

De snelste manier om met Stimulus aan de slag te gaan is door gebruik te maken van dit startersproject met een Express-webserver en Babel al aangesloten. Het hangt ook af van Yarn, dus zorg ervoor dat je het installeert. Om het project te klonen en alle afhankelijkheden te installeren, voert u het volgende uit:

git clone https://github.com/stimulusjs/stimulus-starter.git cd stimulus-starter garen installeren

Als u liever niets lokaal installeert, kunt u dit project op Glitch remixen en alle codering in uw browser uitvoeren.

Geweldig - we zijn er helemaal klaar voor en kunnen doorgaan naar de volgende sectie!

Een beetje markup

Stel dat we een kleine applicatie voor één pagina maken die een lijst met werknemers presenteert en informatie laadt zoals hun naam, foto, functie, salaris, geboortedatum, enz..

Laten we beginnen met de lijst met werknemers. Alle markeringen die we gaan schrijven, moeten in de public / index.html bestand, dat al enkele zeer minimale HTML bevat. Voor nu zullen we al onze medewerkers op de volgende manier hard coderen:

 

Onze werknemers

  • John Doe
  • Alice Smith
  • Will Brown
  • Ann Gray

Leuk! Laten we nu een vleugje Stimulus-magie toevoegen.

Een controller maken

Zoals de officiële documentatie uitlegt, is het belangrijkste doel van Stimulus om JavaScript-objecten (genaamd controllers) naar de DOM-elementen. De controllers brengen de pagina vervolgens tot leven. Als een conventie zouden de namen van controllers moeten eindigen met een _controller postfix (wat heel bekend zou moeten zijn bij ontwikkelaars van Rails).

Er is een directory voor controllers al beschikbaar genaamd src / controllers. Binnenin zul je een vinden  hello_controller.js bestand dat een lege klasse definieert:

import Controller uit "stimulus" export standaardklasse verlengt Controller  

Laten we de naam van dit bestand wijzigen in employees_controller.js. We hoeven dit niet specifiek te vereisen omdat controllers automatisch worden geladen dankzij de volgende coderegels in de src / index.js het dossier:

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

De volgende stap is om onze controller aan te sluiten op de DOM. Om dit te doen, stelt u a in data-controller attribuut en wijs er een identifier aan toe (dat is werknemers in ons geval):

Dat is het! De controller is nu verbonden met de DOM.

Lifecycle-callbacks

Een belangrijk ding om te weten over controllers is dat ze drie levenscyclus callbacks hebben die op specifieke voorwaarden worden geactiveerd:

  • initialiseren: dit terugbellen gebeurt slechts één keer, wanneer de controller wordt geïnstantieerd.
  • aansluiten: vuurt telkens wanneer we de controller verbinden met het DOM-element. Aangezien een controller mogelijk is verbonden met meerdere elementen op de pagina, kan deze callback meerdere keren worden uitgevoerd.
  • Loskoppelen: zoals u waarschijnlijk wel hebt geraden, wordt deze callback uitgevoerd wanneer de controller de verbinding met het DOM-element verbreekt.

Niets ingewikkelds, toch? Laten we profiteren van de initialiseren () en aansluiten() callbacks om ervoor te zorgen dat onze controller echt werkt:

// src / controllers / employees_controller.js export standaardklasse verlengt Controller initialize () console.log ('Geïnitialiseerd') console.log (this) connect () console.log ('Connected') console.log ( deze)  

Start de server vervolgens met:

garen start

Navigeren naar http: // localhost: 9000. Open de console van uw browser en controleer of beide berichten worden weergegeven. Het betekent dat alles werkt zoals verwacht!

Gebeurtenissen toevoegen

Het volgende Core Stimulus-concept is events. Gebeurtenissen worden gebruikt om te reageren op verschillende gebruikersacties op de pagina: klikken, zweven, scherpstellen, enz. Stimulus probeert geen fiets opnieuw uit te vinden en het gebeurtenissysteem is gebaseerd op generieke JS-evenementen.

Laten we bijvoorbeeld een klikgebeurtenis aan onze werknemers binden. Telkens wanneer dit gebeurt, zou ik het nog bestaande niet willen noemen Kiezen() methode van de employees_controller:

 
  • werknemers # kies "> John Doe
  • werknemers # kies "> Alice Smith
  • werknemers # kiezen "> Will Brown
  • werknemers # kies "> Ann Gray

Waarschijnlijk kun je begrijpen wat er hier alleen aan de hand is.

  • data-actie is het speciale kenmerk dat een gebeurtenis aan het element bindt en uitlegt welke actie moet worden aangeroepen.
  • Klik, natuurlijk, is de naam van het evenement.
  • werknemers is het ID van onze controller.
  • Kiezen is de naam van de methode die we graag willen bellen.

Sinds Klik is de meest voorkomende gebeurtenis, deze kan veilig worden weggelaten:

  • John Doe
  • In dit geval, Klik zal impliciet worden gebruikt.

    Laten we vervolgens de code coderen Kiezen() methode. Ik wil niet dat de standaardactie plaatsvindt (wat uiteraard een nieuwe pagina opent die is opgegeven in de href kenmerk), dus laten we dit voorkomen:

    // src / controllers / employees_controller.js // callbacks hier ... kies (e) e.preventDefault () console.log (this) console.log (e)

    e is het speciale gebeurtenisobject dat volledige informatie bevat over de geactiveerde gebeurtenis. Let op, trouwens deze retourneert de controller zelf, geen individuele link! Als u toegang wilt krijgen tot het element dat fungeert als doelwit van de gebeurtenis, gebruikt u e.target.

    Laad de pagina opnieuw, klik op een lijstitem en observeer het resultaat!

    Werken met de staat

    Nu we een klikgebeurtenishandler aan de werknemers hebben gebonden, wil ik de momenteel gekozen persoon opslaan. Waarom? Nadat we deze informatie hebben opgeslagen, kunnen we voorkomen dat dezelfde medewerker de tweede keer wordt geselecteerd. Hierdoor kunnen we later voorkomen dat dezelfde informatie ook meerdere keren wordt geladen.

    Stimulus geeft ons de opdracht de status-API in stand te houden, wat redelijk lijkt. Laten we allereerst enkele willekeurige id's opgeven voor elke medewerker die de data-id attribuut:

     
    • John Doe
    • werknemers # kies "> Alice Smith
    • werknemers # kiezen "> Will Brown
    • werknemers # kies "> Ann Gray

    Vervolgens moeten we het ID ophalen en dit blijven doen. Het gebruik van de Data-API komt heel vaak voor met Stimulus, dus een special Deze data object is beschikbaar voor elke controller. Met zijn hulp kunnen we de volgende methoden gebruiken:

    • this.data.get ( 'name'): haal de waarde op basis van zijn attribuut.
    • this.data.set ('naam', waarde): stel de waarde in onder een attribuut.
    • this.data.has ( 'name'): controleer of het attribuut bestaat (geeft een Booleaanse waarde terug).

    Helaas zijn deze snelkoppelingen niet beschikbaar voor de doelen van de klikgebeurtenissen, dus we moeten ons houden aan getAttribute () in hun geval:

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

    Maar we kunnen het nog beter doen door een getter en een setter te maken voor de huidige werknemer:

     // src / controllers / employees_controller.js krijgen currentEmployee () return this.data.get ("current-employee") stel currentEmployee (id) if (this.currentEmployee! == id) this.data.set in ("huidige werknemer", id)

    Merk op hoe we de gebruiken this.currentEmployee getter en ervoor te zorgen dat het opgegeven ID niet hetzelfde is als het reeds opgeslagen ID.

    Nu mag je de Kiezen() methode op de volgende manier:

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

    Laad de pagina opnieuw om ervoor te zorgen dat alles nog steeds werkt. U zult nog geen visuele wijzigingen opmerken, maar met behulp van de Inspector-tool zult u merken dat het ul heeft de data-werknemers-current-werknemer attribuut met een waarde die verandert als je op de links klikt. De werknemers deel in de naam van het attribuut is de identifier van de controller en wordt automatisch toegevoegd.

    Laten we nu verder gaan en de momenteel gekozen werknemer benadrukken.

    Gebruik van doelen

    Wanneer een werknemer is geselecteerd, zou ik het overeenkomstige element willen toewijzen met een .uitgekozen klasse. Natuurlijk kunnen we deze taak hebben opgelost door enkele JS-selectorfuncties te gebruiken, maar Stimulus biedt een nettere oplossing.

    Ontmoet doelen, waarmee u een of meer belangrijke elementen op de pagina kunt markeren. Deze elementen kunnen vervolgens gemakkelijk worden geopend en gemanipuleerd wanneer nodig. Voeg een toe om een ​​doel te creëren data-target attribuut met de waarde van Controller. Target_name (dat heet a doel descriptor):

     
    • John Doe
    • werknemers # kies "> Alice Smith
    • werknemers # kiezen "> Will Brown
    • werknemers # kies "> Ann Gray

    Laat Stimulus nu over deze nieuwe doelen weten door een nieuwe statische waarde te definiëren:

    // src / controllers / employees_controller.js export standaardklasse verlengt Controller static targets = ["employee"] // ...

    Hoe krijgen we nu toegang tot de doelen? Het is zo simpel als zeggen this.employeeTarget (om het eerste element te krijgen) of this.employeeTargets (om alle elementen te krijgen):

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

    Super goed! Hoe kunnen deze doelen ons nu helpen? Welnu, we kunnen ze gebruiken om op eenvoudige wijze CSS-klassen toe te voegen en te verwijderen op basis van een aantal criteria:

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

    Het idee is simpel: we herhalen verschillende doelen en vergelijken elk doel data-id naar degene die is opgeslagen onder this.currentEmployee. Als het overeenkomt, krijgt het element de .uitgekozen klasse. Anders wordt deze klasse verwijderd. Je kunt ook het if (this.currentEmployee! == id) conditie van de setter en gebruik deze in de gekozen () methode in plaats daarvan:

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

    Ziet er goed uit! Ten slotte zullen we een aantal zeer eenvoudige styling bieden voor de .uitgekozen klasse binnen de public / main.css:

    .gekozen font-weight: bold; tekstdecoratie: geen; cursor: standaard; 

    Laad de pagina opnieuw opnieuw, klik op een persoon en zorg ervoor dat die persoon goed wordt gemarkeerd.

    Gegevens asynchroon laden

    Onze volgende taak is om informatie over de gekozen werknemer te laden. In een toepassing in de echte wereld zou je een hostingprovider moeten opzetten, een back-end die wordt aangedreven door Django of Rails, en een API-eindpunt dat reageert met JSON met alle benodigde gegevens. Maar we gaan het een beetje eenvoudiger maken en ons alleen concentreren op de klant. Creëer een werknemers map onder de openbaar map. Voeg vervolgens vier bestanden met gegevens voor individuele werknemers toe:

    1.json

    "naam": "John Doe", "geslacht": "man", "leeftijd": "40", "positie": "CEO", "salaris": "$ 120.000 / jaar", "afbeelding": "https : //burst.shopifycdn.com/photos/couple-in-love-at-sunset_373x.jpg " 

    2.json

    "name": "Alice Smith", "gender": "female", "age": "32", "position": "CTO", "salary": "$ 100.000 / year", "image": "https : //burst.shopifycdn.com/photos/woman-listening-at-team-meeting_373x.jpg " 

    3.json

    "naam": "Will Brown", "geslacht": "mannelijk", "leeftijd": "30", "positie": "Tech Lead", "salaris": "$ 80.000 / jaar", "afbeelding": " https://burst.shopifycdn.com/photos/casual-urban-menswear_373x.jpg " 

    4.json

    "name": "Ann Gray", "gender": "female", "age": "25", "position": "Junior Dev", "salary": "$ 20.000 / year", "image": " https://burst.shopifycdn.com/photos/woman-using-tablet_373x.jpg " 

    Alle foto's zijn genomen van de gratis stockfotografie door Shopify genaamd Burst.

    Onze gegevens staan ​​klaar om te worden geladen! Om dit te doen, coderen we een afzonderlijke code loadInfoFor () methode:

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

    Deze methode accepteert de ID van een werknemer en verzendt een asynchrone ophaalopdracht naar de gegeven URI. Er zijn ook twee beloften: een om het lichaam op te halen en een ander om de geladen info weer te geven (we zullen de corresponderende methode in een moment toevoegen).

    Gebruik deze nieuwe methode binnen Kiezen():

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

    Voor het coderen van de displayInfo () methode hebben we een element nodig om de gegevens daadwerkelijk te renderen. Waarom maken we niet opnieuw gebruik van doelen??

     

    Definieer het doel:

    // src / controllers / employees_controller.js export standaardklasse verlengt Controller static targets = ["employee", "info"] // ...

    En gebruik het nu om alle info weer te geven:

     // src / controllers / employees_controller.js displayInfo (raw_json) const info = JSON.parse (raw_json) const html = '
    • Naam: $ info.name
    • Geslacht: $ info.gender
    • Leeftijd: $ info.age
    • Positie: $ info.position
    • Salaris: $ info.salary
    'this.infoTarget.innerHTML = html

    Natuurlijk ben je vrij om een ​​sjablonerende motor zoals Handlebars te gebruiken, maar voor dit eenvoudige geval zou dat waarschijnlijk overkill zijn.

    Laad nu de pagina opnieuw en kies een van de werknemers. Zijn bio en afbeelding moeten vrijwel onmiddellijk worden geladen, wat betekent dat onze app naar behoren werkt!

    Dynamische lijst van werknemers

    Met behulp van de hierboven beschreven aanpak kunnen we zelfs verder gaan en de lijst met medewerkers direct laden in plaats van deze hard-coderen.

    Bereid de gegevens in de public / employees.json het dossier:

    ["id": "1", "name": "John Doe", "id": "2", "name": "Alice Smith", "id": "3", "name ":" Will Brown ", " id ":" 4 "," name ":" Ann Gray "]

    Nu tweak public / index.html bestand door de hardcoded lijst te verwijderen en een data-werknemers-url kenmerk (merk op dat we de naam van de controller moeten vermelden, anders werkt de Data API niet):

    Zodra de controller is aangesloten op de DOM, moet deze een ophaalopdracht verzenden om een ​​lijst met werknemers samen te stellen. Het betekent dat de aansluiten() callback is de perfecte plaats om dit te doen:

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

    Ik stel voor dat we meer generiek creëren loadFrom () methode die een URL accepteert om gegevens van te laden en een callback om deze gegevens daadwerkelijk weer te geven:

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

    Tweak de Kiezen() methode om te profiteren van de loadFrom ():

     // src / controllers / employees_controller.js kies (e) e.preventDefault () const id = e.target.getAttribute ('data-id') if (this.currentEmployee! == id) this.loadFrom (' werknemers / $ id .json ', this.displayInfo) // <--- this.currentEmployee = id this.employeeTargets.forEach((el, i) => el.classList.toggle ("selected", id === el.getAttribute ("data-id")))

    displayInfo () kan ook worden vereenvoudigd, omdat JSON nu in de hele map wordt geparseerd loadFrom ():

     // src / controllers / employees_controller.js displayInfo (info) const html = '
    • Naam: $ info.name
    • Geslacht: $ info.gender
    • Leeftijd: $ info.age
    • Positie: $ info.position
    • Salaris: $ info.salary
    'this.infoTarget.innerHTML = html

    Verwijderen loadInfoFor () en codeer de displayEmployees () methode:

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

    Dat is het! We geven nu onze lijst met werknemers dynamisch weer op basis van de gegevens die door de server worden geretourneerd.

    Conclusie

    In dit artikel hebben we een bescheiden JavaScript-framework behandeld, genaamd Stimulus. We hebben gezien hoe een nieuwe applicatie te maken, een controller met een heleboel callbacks en acties toe te voegen en evenementen en acties te introduceren. We hebben ook wat asynchrone gegevens geladen met behulp van ophaalopdrachten.

    Al met al, dat is het voor de basisprincipes van Stimulus - het verwacht echt niet dat je wat mysterieuze kennis hebt om webtoepassingen te maken. Natuurlijk zal het raamwerk in de toekomst waarschijnlijk een aantal nieuwe functies hebben, maar de ontwikkelaars zijn niet van plan om er een enorm monster van te maken met honderden gereedschappen. 

    Als je meer voorbeelden wilt vinden van het gebruik van Stimulus, kijk dan ook eens naar dit kleine handboek. En als u op zoek bent naar aanvullende JavaScript-bronnen om te studeren of te gebruiken in uw werk, bekijk dan wat we beschikbaar hebben op de Envato-markt. 

    Vond je Stimulus leuk? Zou u geïnteresseerd zijn in het proberen om een ​​real-world toepassing te creëren die mogelijk wordt gemaakt door dit framework? Deel uw mening in de commentaren!

    Zoals altijd bedank ik je dat je bij me bent gebleven en tot de volgende keer.