Съвети и трикове за създаване на компоненти за повторно използване на потребителски интерфейс

Снимка на Farzard Nazifi

В тази статия искам да споделя някои съвети и трикове, които използвам, докато изграждам нашата основна библиотека за фронтенд, използвайки Ember.js. Като преди това нямаше контакт с него, това беше чудесна възможност за учене. Надявам се, момчета да ви хареса! Моля, обърнете внимание, че кодът, използван за илюстриране на идеите в статията, съдържа достатъчно информация, за да разгледате точката. Той също използва някои терминологии на Ember.js, но понятията са предназначени да бъдат рамково-агностични.

Целите

По-просто казано, изискванията за изграждане на библиотеката са следните:

  1. Трябва да е продуктивен.
  2. Тя трябва да бъде поддържаема.
  3. Тя трябва да е последователна.

Подходите

Минимизиране на бизнес логиката

Един от най-честите проблеми, които срещам при проектите, са компоненти, които съдържат твърде много логика в тях. По този начин, изпълнение на задачи, които теоретично са извън техния обхват.

Преди да внедрите някоя функционалност, е добре да очертаете някои от задълженията, за които компонентът отговаря.

Представете си, че изграждаме компонент с бутони.

Бих искал да мога да:

  • Информирайте за кой тип бутон е - Основен или редовен
  • Информирайте съдържанието, показано вътре в бутона (Икона и текст)
  • Деактивирайте или активирайте бутона
  • Извършете някакво действие при кликване

Имайки този малък контур, разделете различните части, участващи в процеса на изграждане на този компонент. Опитайте се да определите къде могат да бъдат поставени нещата.

1 - Типът и съдържанието са специфични за компонента, така че те могат да бъдат поставени в компонентния файл.

Тъй като типът до известна степен е необходим, нека добавим потвърждение в случай, че не е предоставена стойност.

const type = get (това, 'type');
типове const = {
  първичен: 'btn - първичен',
  редовен: 'btn - редовен',
}
връщане (тип)? видове [тип]: types.regular;

Харесва ми картографирането на свойствата в обект, защото позволява нещата да се мащабират без много усилия - в случай, че се нуждаем от бутон за опасност или нещо подобно.

2 - Деактивираното състояние може да се намери на различни компоненти като вход. За да се избегне повторение, това поведение може да бъде преместено в модул или всяка споделена структура - хората го наричат ​​миксин.

3 - Действието на щракване може да се намери в различни компоненти. Така че той също може да бъде преместен в друг файл и не трябва да съдържа никаква логика в него - просто извикване на обратния разговор, предоставен от разработчика.

По този начин можем да имаме представа в кои случаи трябва да се занимава нашия компонент, като същевременно помага да се очертае базова архитектура, която поддържа разширение.

Отделно състояние за многократно използване на потребителския интерфейс

Някои взаимодействия с потребителски интерфейс са често срещани между различни компоненти, като:

  • Активиране / деактивиране - напр. бутони, входове
  • Разширяване / свиване - напр. свиване, падащи списъци
  • Показване / скриване - Почти всичко

Тези свойства често се използват само за контрол на визуалното състояние - да се надяваме.

Поддържайте последователна номенклатура в различните компоненти. Всички действия, свързани с визуално състояние, могат да бъдат преместени в миксин.

/ * UIStateMixin * /
забрани () {
  set (това, „забранено“, вярно);
  върнете това;
}
enable () {
  set (това, „забранено“, невярно “);
  върнете това;
}

Всеки метод е отговорен само за превключване на определена променлива и връща текущия контекст за свързване, като:

бутон
  .disable ()
  .showLoadingIndicator ();

Този подход може да бъде разширен. Той може да приема различни контексти и да контролира външни променливи, вместо да използва вътрешни. Например:

_getCurrentDisabledAttr () {
  return (isPresent (get (това, „инвалиди“)))
    ? 'забранено' / * Външен параметър * /
    : 'isDisabled'; / * Вътрешна променлива * /
}
enable (контекст) {
  set (контекст || това, това._getCurrentDisabledAttr (), невярно);
  върнете това;
}

Абстрахиране на основни функционалности

Всеки компонент съдържа определени подпрограми. Тези процедури трябва да се изпълняват независимо от целта на компонента. Например, проверка на обратното обаждане преди задействането му.

Тези методи по подразбиране могат също да бъдат преместени в техните собствени комбинации, като така:

/ * BaseComponentMixin * /
_isCallbackValid (callbackName) {
  const callback = get (това, callbackName);
  
  връщане !! (isPresent (обратно извикване) && typeof callback === 'функция');
}
_handleCallback (обратно извикване, парами) {
  if (! this._isCallbackValid (обратно повикване)) {
    хвърли нова грешка (/ * съобщение * /);
  }
  this.sendAction (обратно извикване, парами);
}

И след това са включени в компонентите.

/* Компонент */
onClick (парами) {
  this._handleCallback ('onClick', парами);
}

Това поддържа базовата архитектура последователна. Той също така позволява разширяване и дори интеграция със софтуер на трети страни. Но моля ви, не бъдете философстващ абстрактер.

Съставяне на компоненти

Избягвайте функциите за пренаписване, доколкото можете. Може да се постигне специализация. Може да се направи чрез композиция и групиране. Както и да свържете по-малки компоненти заедно, за да създадете нови компоненти.

Например:

Основни компоненти: Бутон, падащо меню, вход.
Бутон за падане => бутон + падащо меню
Автоматично довършване => въвеждане + падащо меню
Изберете => въвеждане (само за четене) + падащо меню

По този начин всеки компонент има свои задължения. Всеки обработва собственото си състояние и параметри, докато компонентът на обвивката обработва специфичната си логика.

Разделяне на тревогите най-добре.

Разделяне на тревогите

Когато съставяте по-сложни компоненти, съществува възможност за разделяне на проблемите. Можете да разделите притесненията между различните части на компонента

Да речем, че изграждаме избран компонент.

{{формуляр-избор на свързване = productId items = items}}
елементи = [
  {описание: 'Продукт №1', стойност: 1},
  {описание: 'Продукт №2', стойност: 2}
]

Вътрешно имаме прост входен компонент и падащо меню.

{{свързване на формуляра = _пис}}}
{{ui-dropdown items = items onSelect = (действие 'selectItem')}}

Основната ни задача е да представим описанието на потребителя, но то няма значение за нашето приложение - стойността го прави.

Когато избирате опция, разделяте обекта, изпращайки описанието надолу към нашия вход чрез вътрешна променлива, докато натискате стойността нагоре към контролера, актуализирайки свързаната променлива.

Тази концепция може да се приложи за компоненти, където свързаната стойност трябва да се трансформира, като число, автоматично завършване или поле за избор. Datepickers също могат да прилагат това поведение. Те могат да демаскират датата, преди да актуализират свързаната променлива, докато представят маскираната стойност на потребителя.

Рисковете стават по-високи, когато трансформациите се увеличават в сложността. Чрез прекомерна логика или трябва да подкрепяте събития - така че помислете, преди да приложите този подход.

Пресети срещу нови компоненти

Понякога е необходимо да се оптимизират компоненти и услуги, за да се улесни развитието. Те се доставят под формата на предварителни настройки или нови компоненти.

Пресетите са параметри. Когато са информирани, те задават предварително зададени стойности на компонента, опростявайки декларацията му. Но новите компоненти обикновено са по-специализирани версии на базови компоненти.

Трудната част е да знаете кога да внедрите пресети или да създадете нови компоненти. Използвам следните указания, когато вземам това решение:

Кога да създадете предварително зададени настройки

1 - Повтарящи се модели на използване

Има моменти, когато определен компонент се използва повторно на различни места със същите параметри. В тези случаи обичам да предпочитам предварително зададени настройки пред нови компоненти, особено когато базовият компонент има прекалено голям брой параметри.

/ * Редовно изпълнение * /
{{Формата автоматично довършване
    свързване = ProductID
    url = "продукти" / * URL адрес за получаване * /
    labelAttr = "описание" / * Атрибут, използван като етикет * /
    valueAttr = "id" / * Атрибут, използван като стойност * /
    apiAttr = "продукт" / * Param изпратен при поискване * /
}}
/ * Предварителни настройки * /
{{Формата автоматично довършване
    запаметена = "продукт"
    свързване = ProductID
}}

Стойностите от предварително зададените настройки се задават само ако параметърът не е информиран, запазвайки неговата гъвкавост.

/ * Наивна реализация на предварително зададения модул * /
const presets = {
  продукт: {
    URL адрес: „продукти“,
    labelAttr: „описание“,
    valueAttr: „id“,
    apiAttr: „продукт“,
  }
}
const attrs = пресети [get (това, 'preset')];
Object.keys (attrs) .forEach ((prop) => {
  if (! get (this, prop)) {
    набор (това, опора, attrs [prop]);
  }
});

Този подход намалява знанията, необходими за персонализиране на вашия компонент. Едновременно с това той улеснява поддръжката, като ви позволява да актуализирате стойностите по подразбиране на едно място.

2 - Основният компонент е твърде сложен

Когато базовият компонент, който ще използвате за създаване на по-конкретен компонент, приема твърде много параметри. Така създаването му би породило някои проблеми. Например:

  • Ще трябва да въведете повечето - ако не всички - параметри от новия компонент до базовия компонент. Тъй като все повече и повече компоненти произтичат от него, всяка актуализация на основния компонент би отразявала огромно количество промени. По този начин, което води до по-висока честота на бъгове.
  • Колкото повече компоненти се създават, толкова по-трудно е да се документират и запомнят различните нюанси. Това важи особено за новите разработчици.

Кога да създадете нови компоненти

1 - Разширяване на функционалността

Възможно е да се създаде нов компонент при разширяване на функционалността от по-прост компонент. Помага ви да предотвратите изтичане на специфична логика на компонента към друг компонент. Това е особено полезно, докато прилагате допълнително поведение.

/ * Декларация * /
{{ui-button-dropdowndown items = items}}
/* Под капака */
{{# ui-бутон onClick = (действие 'toggleDropdown')}}
  {{label}}  
{{/ UI бутон}}
{{#if isExpanded}}
  {{ui-падащи елементи = елементи}}
{{/ Ако}}

Примерът по-горе използва компонента на бутона. Това разширява оформлението му, за да поддържа фиксирана икона, докато включва падащ компонент и състоянието му на видимост.

2 - Параметри за декориране

Има и друга възможна причина за създаването на нови компоненти. Това е когато е необходимо да се контролира наличността на параметъра или да се декорират стойностите по подразбиране.

/ * Декларация * /
{{form-datepicker onFocus = (действие 'doSomething')}}
/* Под капака */
{{form-input onFocus = (действие '_onFocus')}}
_onFocus () {
  $ (This.element)
    .find ( "вход")
    .Изберете (); / * Изберете стойност на полето на фокус * /
  this._handleCallback ( "onFocus '); / * Задейства обратно извикване на парам * /
}

В този пример беше предоставена на компонента функция, която трябваше да бъде извикана, когато полето е фокусирано.

Вътрешно, вместо да прехвърли обратния разговор направо към основния компонент, той преминава вътрешна функция. Това изпълнява определена задача (избиране на стойността на полето) и след това извиква предоставения обратно повикване.

Не е пренасочване на всички параметри, приети от основния входен компонент. Това помага да се контролира обхвата на определени функционалности. Освен това се избягват ненужни проверки.

В моя случай събитието onBlur беше заменено от друго събитие - onChange. Това се задейства, когато потребителят или попълни полето, или избере дата в календара.

заключение

Когато изграждате вашите компоненти, помислете за вашата страна, както и за това, който използва този компонент в ежедневието си. По този начин всеки печели.

Най-добрият резултат идва от всички в групата, които правят това, което е най-добро за себе си и групата - Джон Наш

Също така, не се срамувайте да поискате отзиви. Винаги ще намерите нещо, върху което може да се работи.

За да изострите още повече своите умения за софтуерно инженерство, препоръчвам да следвате поредицата на Ерик Елиът „Съставяне на софтуер“. Това е страхотно!

Е, надявам се, че ви е харесала статията. Моля, вземете тези концепции, превърнете се в свои собствени идеи и ги споделете с нас!

Също така, не се колебайте да се свържете с мен в Twitter @gcolombo_! Бих искал да чуя вашето мнение и дори да работим заедно.

Благодаря!