Savjeti i trikovi za stvaranje komponenti korisničkog sučelja za višekratnu upotrebu

Fotograf Farzard Nazifi

U ovom članku želim podijeliti nekoliko savjeta i trikova koje koristim za izradu naše osnovne knjižnice frontenda pomoću Ember.js. Bez kontakta s njim ranije, to je bila sjajna prilika za učenje. Nadam se da ćete uživati ​​u tome! Imajte na umu, kôd koji se koristi za primjeru ideja iz članka sadrži dovoljno informacija da biste dobili poantu. Također koristi određenu terminologiju Ember.js, ali koncepti bi trebali biti okvirno-agnostički.

Ciljevi

Jednostavno, zahtjevi za izgradnju knjižnice su sljedeći:

  1. Mora biti produktivan.
  2. To mora biti održivo.
  3. Mora biti dosljedan.

Pristupi

Smanjite poslovnu logiku

Jedan od najčešćih problema s kojima se susrećem na projektima jesu komponente koje u njima sadrže previše logike. Dakle, obavljanje zadataka koji su, teoretski, izvan njihovog dosega.

Prije primjene bilo koje funkcionalnosti, dobro je navesti neke dužnosti za koje je komponenta odgovorna.

Zamislite da gradimo komponentu gumba.

Želio bih biti u mogućnosti da:

  • Obavijestite o kojoj se vrsti gumba radi - primarnoj ili redovnoj
  • Informišite sadržaj prikazan unutar gumba (ikona i tekst)
  • Onemogućite ili omogućite gumb
  • Izvršite neku radnju nakon klika

Imajući ovaj mali obris, razdvojite različite dijelove koji su uključeni u proces izgradnje ove komponente. Pokušajte odrediti gdje se stvari mogu smjestiti.

1 - Vrsta i sadržaj specifični su za komponente, tako da se mogu smjestiti u datoteku komponenata.

Budući da je vrsta - u određenoj mjeri - potrebna, dodajmo potvrdu u slučaju da nije data vrijednost.

const type = get (ovo, 'vrsta');
vrste const = {
  primarno: 'btn - primarna',
  redovito: 'btn - redovito',
}
povrat (vrsta)? vrste [vrsta]: vrste.regularno;

Sviđa mi se preslikavanje svojstava u objekt jer omogućava da se stvari skaliraju bez mnogo napora - u slučaju da nam treba gumb za opasnost ili nešto slično.

2 - Onemogućeno stanje može se naći na različitim komponentama poput ulaza. Kako bi se izbjeglo ponavljanje, ovo se ponašanje može premjestiti u modul ili bilo koju zajedničku strukturu - ljudi to zovu miksin.

3 - Radnja klika nalazi se u različitim komponentama. Tako se ona također može premjestiti u drugu datoteku i ne bi trebala sadržavati logiku unutar nje - jednostavno pozivanje povratnog poziva koji je dao programer.

Na ovaj način možemo imati predstavu u kojim se slučajevima naša komponenta treba baviti, pomažući pricrtati baznu arhitekturu koja podržava proširenje.

Odvojeno korisničko sučelje za višekratnu upotrebu

Određene interakcijske interakcije uobičajene su među različitim komponentama, poput:

  • Omogućiti / onemogućiti - npr. tipke, ulazi
  • Proširiti / smanjiti - npr. kolaps, padajuće liste
  • Prikaži / sakrij - Prilično sve

Ova se svojstva često koriste samo za kontrolu stanja vida - nadamo se.

Održavajte dosljednu nomenklaturu u različitim komponentama. Sve akcije povezane s vizualnim stanjem mogu se premjestiti u miks.

/ * UIStateMixin * /
onesposobiti () {
  set (ovo, 'onemogućeno', istina);
  vrati ovo;
}
enable () {
  skup (ovo, 'onemogućeno', lažno ');
  vrati ovo;
}

Svaka je metoda odgovorna samo za prebacivanje određene varijable i vraća trenutni kontekst za vezivanje, poput:

dugme
  .disable ()
  .showLoadingIndicator ();

Taj se pristup može proširiti. Može prihvaćati različite kontekste i upravljati vanjskim varijablama umjesto korištenja unutarnjih. Na primjer:

_getCurrentDisabledAttr () {
  return (isPresent (get (ovo, 'onemogućeno')))
    ? 'onesposobljeno' / * Vanjski parametar * /
    : 'je onemogućeno'; / * Unutarnja varijabla * /
}
omogućiti (kontekst) {
  postavi (kontekst || ovo, ovo._getCurrentDisabledAttr (), netočno);
  vrati ovo;
}

Sažetak osnovnih funkcionalnosti

Svaka komponenta sadrži određene rutine. Te se rutine moraju izvoditi bez obzira na svrhu komponente. Na primjer, provjeravanje povratnog poziva prije pokretanja.

Ove se zadane metode mogu premjestiti u svoje kombinacije, poput:

/ * BaseComponentMixin * /
_isCallbackValid (callbackName) {
  const povratni poziv = get (ovo, callbackName);
  
  povratak !! (isPresent (povratni poziv) && typeof callback === 'funkcija');
}
_handleCallback (povratni poziv, parame) {
  if (! this._isCallbackValid (povratni poziv)) {
    baciti novu Pogrešku (/ * poruka * /);
  }
  this.sendAction (povratni poziv, parame);
}

A zatim uključeni u komponente.

/ * Komponenta * /
onClick (params) {
  this._handleCallback ('onClick', params);
}

To održava dosljednu vašu arhitekturu. Također omogućuje proširenje, pa čak i integraciju sa softverom treće strane. Ali molim vas, nemojte biti filozofski apstraktni.

Sastavljanje sastavnih dijelova

Izbjegavajte prepisivanje funkcionalnosti koliko god možete. Može se postići specijalizacija. Može se postići sastavom i grupiranjem. Kao i uklapanje manjih komponenata zajedno u cilju stvaranja novih komponenti.

Na primjer:

Osnovne komponente: Gumb, padajući izbor, ulaz.
Gumb za padanje => gumb + padajući gumb
Automatsko dovršavanje => unos + pada
Odaberite => ulaz (samo za čitanje) + padajući izbornik

Na taj način svaka komponenta ima svoje zadatke. Svaka obrađuje svoje stanje i parametre, dok komponenta omota obrađuje svoju specifičnu logiku.

Razdvajanje problema u najboljem redu.

Podjela briga

Kada sastavljate složenije komponente, postoji mogućnost dijeljenja briga. Možete podijeliti brige između različitih dijelova komponente

Recimo da gradimo odabranu komponentu.

{{oblik-odabir obvezujući = productId items = predmeti}}
predmeti = [
  {opis: "Proizvod br. 1", vrijednost: 1},
  {opis: 'Proizvod br. 2', vrijednost: 2}
]

Interno imamo jednostavnu ulaznu komponentu i padajući izbornik.

{{obrazac za unos oblika = _ opis}}
{{ui-padajuće stavke = stavke onSelect = (radnja 'selectItem')}}

Naš je glavni zadatak predstaviti opis korisniku, ali on nema značenja za našu aplikaciju - vrijednost jest.

Prilikom odabira opcije razdvajate objekt, slanjem opisa na naš ulaz putem interne varijable, istodobno gurajući vrijednost do kontrolera, ažurirajući povezanu varijablu.

Ovaj se koncept može primijeniti na komponente u kojima se ograničena vrijednost mora transformirati, poput broja, automatskog dovršavanja ili odabranog polja. Datepickeri također mogu implementirati ovo ponašanje. Oni mogu ukloniti datum prije ažuriranja vezane varijable, a korisniku će predstaviti maskiranu vrijednost.

Rizici postaju sve veći kako transformacije povećavaju složenost. Pretjeranom logikom ili pružanjem podrške događajima - razmislite prije nego što primijenite ovaj pristup.

Predsetsi u odnosu na nove komponente

Ponekad je potrebno optimizirati komponente i usluge da bi se olakšao razvoj. Dostavljaju se u obliku unaprijed postavljenih ili novih komponenti.

Unaprijed postavljene postavke su parametri. Kad su informirani, oni postavljaju unaprijed definirane vrijednosti na komponenti, pojednostavljujući njezinu deklaraciju. Međutim, nove komponente obično su više specijalizirane verzije osnovnih komponenti.

Težak je dio znati kada implementirati unaprijed zadane postavke ili stvoriti nove komponente. Pri donošenju ove odluke koristim sljedeće smjernice:

Kada stvoriti unaprijed zadane postavke

1 - ponavljajući obrasci upotrebe

Postoje slučajevi kad se određena komponenta ponovno upotrebljava na raznim mjestima s istim parametrima. U tim slučajevima volim davati prednost postavljenim postavkama nad novim komponentama, posebno kad bazna komponenta ima preveliki broj parametara.

/ * Redovita primjena * /
{{Oblik-autocomplete
    vezanje = productId
    url = "proizvodi" / * URL koji treba dohvatiti * /
    labelAttr = "opis" / * Atribut koji se koristi kao oznaka * /
    valueAttr = "id" / * Atribut koji se koristi kao vrijednost * /
    apiAttr = "proizvod" / * Param poslan na zahtjev * /
}}
/ * Unaprijed određene postavke * /
{{Oblik-autocomplete
    unaprijed = „proizvod”
    vezanje = productId
}}

Vrijednosti iz unaprijed postavljene vrijednosti postavljaju se samo ako parametar nije informiran, čuvajući njegovu fleksibilnost.

/ * Naivna primjena unaprijed postavljenog modula * /
unaprijed postavljene vrijednosti = {
  proizvod: {
    url: 'proizvodi',
    labelAttr: 'opis',
    valueAttr: 'id',
    apiAttr: "proizvod",
  }
}
const attrs = presets [dobiti (ovo, 'unaprijed')];
Object.keys (attrs) .forEach ((prop) => {
  if (! get (this, prop)) {
    set (ovaj, prop, attrs [prop]);
  }
});

Ovaj pristup smanjuje znanje potrebno za prilagodbu vaše komponente. Paralelno, to olakšava održavanje omogućavanjem ažuriranja zadanih vrijednosti na jednom mjestu.

2 - Osnovna komponenta je previše složena

Kad osnovna komponenta koju koristite za stvaranje određenije komponente prihvaća previše parametara. Dakle, stvaranje bi stvorilo neke probleme. Na primjer:

  • Morate unijeti većinu - ako ne i sve - parametre iz nove u baznu komponentu. Budući da iz njega proizlazi sve više i više komponenti, svako ažuriranje osnovne komponente odražavalo bi ogromnu količinu promjena. Dakle, što dovodi do veće pojave kukaca.
  • Što se više komponenti stvara, to je teže dokumentirati i pamtiti različite nijanse. To se posebno odnosi na nove programere.

Kada stvoriti nove komponente

1 - Proširenje funkcionalnosti

Izgraditi novu komponentu moguće je proširiti funkcionalnost jednostavnijom komponentom. Pomaže vam da spriječite propuštanje logike specifične za komponentu u drugu komponentu. To je osobito korisno tijekom provođenja dodatnog ponašanja.

/ * Izjava * /
{{ui-gumb-padajuće stavke = predmeti}}
/* Ispod haube */
{{# ui-gumb onClick = (radnja 'toggleDropdown')}}
  {{label}}  
{{/ Ui dugme}}
{{#if je Prošireno}}
  {{ui-padajuci predmeti = predmeti}}
{{/ako}}

Gornji primjer koristi komponentu gumba. To proširuje svoj izgled tako da podržava fiksnu ikonu, dok uključuje padajuću komponentu i stanje vidljivosti.

2 - Parametri ukrašavanja

Postoji još jedan mogući razlog za stvaranje novih komponenti. Ovo je kada je potrebno kontrolirati dostupnost parametara ili ukrasiti zadane vrijednosti.

/ * Izjava * /
{{form-datepicker onFocus = (radnja 'doSomething')}}
/* Ispod haube */
{{form-input onFocus = (akcija '_onFocus')}}
_onFocus () {
  $ (This.element)
    .find ( 'ulaz')
    .Izaberi(); / * Odaberite vrijednost polja na fokusu * /
  this._handleCallback ( 'onFocus'); / * Pokreće povratni poziv * /
}

U ovom primjeru komponenti je data funkcija koja se trebala zvati kada je polje fokusirano.

Unutarnje, umjesto da povratni poziv upućuje ravno na baznu komponentu, prelazi unutarnju funkciju. Time se obavlja određeni zadatak (odabir vrijednosti polja), a zatim se dobiva povratni poziv.

Ne preusmjerava sve parametre koje je osnovna ulazna komponenta prihvatila. To pomaže kontrolirati opseg određenih funkcija. Također se izbjegavaju nepotrebne provjere valjanosti.

U mom slučaju događaj onBlur zamijenjen je drugim događajem - onChange. To se pokreće kada korisnik ili ispuni polje ili odabere datum u kalendaru.

Zaključak

Prilikom izrade komponenti razmislite o svojoj strani kao i o tome tko upotrebljava tu komponentu u svakodnevnom životu. Na ovaj način pobjeđuju svi.

Najbolji rezultat dolazi od svih u grupi koji rade ono što je najbolje za sebe i grupu - John Nash

Takođe, nemojte se sramiti tražiti povratne informacije. Uvijek ćete pronaći nešto na čemu se može raditi.

Kako biste još više pooštrili svoje vještine softverskog inženjeringa, preporučujem slijediti seriju Erica Elliotta "Sastavljanje softvera". To je sjajno!

Pa, nadam se da ste uživali u članku. Molimo uzmite ove koncepte, pretvorite se u vlastite ideje i podijelite ih s nama!

Također, slobodno mi se obratite na twitteru @gcolombo_! Volio bih čuti vaše mišljenje, pa čak i raditi zajedno.

Hvala!