Галоўная / Артыкулы / Дзе палягае межа фрэймворку ў стаке UI, які можна перадаць знову

Дзе палягае межа фрэймворку ў стаке UI, які можна перадаць знову

Дазвольце дазнацца, як машыны станоў, веб-компаненты, а таксама макетаванне і рух на аднойчыні з атрыбутамі дапамагаюць керуванню інтэрфейсам выжыць палягліва ўніз ад фрэймворка, і калі такая переноснасць не ўзрабатвае.

3630 слоў

Большасць команд дужа добра рашуюць кантэкст корыстніка для аднаго-ўсего фрэймворку. Рэтельна створаны набор компонентаў Angular, сэт прымітываў React, система дизайна, прыўязаная да ціклу жыцця аднаго рэндерара — усё гэта працуе чымала добра, пакуль проекту не патрабуецца інструмент іншага типу, і тады практычна нічога з усіх гэтых інвестыцый не застаецца. Разбіўка відновляемага кантэксту корыстніка на шары (паведанне, рэндаруемыя компоненты і маленькія можлівасці HTML, такія як макетаванне і рух) дазволяе цярпяча выбраць, якія шары трэба прыўязаць да фрэймворку, а якія — ні.

Проблема рашэння кантэксту корыстніка толькі для аднаго фрэймворку

Уявіце сабэкспертную каманду, якая большую частку свага часу прыкладвае да Angular і падобаецца там, куды рухаецца гэты фреймворк: сігналы, савэцькія API і многа структуры для прыладоў, якім гэта неабходна. Їхні внутршні набор аксэсуараў адпавядае модэлю shadcn, пад якой кантэнт компонентаў копіюецца ў кожны проект і стае яго часткай, а для роботы на низкаму рэвэлі ўжываюцца спецыяльныя прымітівы Angular. Унутр у прыладу на Angular гэта ўзначны варыянт налашоўкі.

Проблемы пачатваюцца, калі наступны проект не ў формате Angular. Большая, з великім колістаннем станоў, з актываюцыміся элементамі аплікацыя можа ідеальна падходзіць для Angular. Большасцю статычныя маркетынгавыя сайты часта краща працуюць з Astro. Інодзе сама платформа браузера вже забезпечвае майжа ўсё, што трэба. Якщо тэхналогія должна прыстаць да трэбаванняў кожнага проекту, а не навпакі, тады выйшае нэвыхідныя запитанне:

Якую частку вашай інфраструктуры UI трэба застаўіць, калі меняецца фрэймворк?

Падчыненне гэтаму пытанню зазвычай ведае через рэштку ідэй: спачатку Web Components, потым headless UI, пасля — машыны стану, і нарэшце — менш традыцыйны эксперымент, у яком распаковка і рух атрыбутуюцца сабе самымі HTML-атрыбутамі. Спачатку яны здаюцца незв’язанымі. Але калі рассмотрыць іх разам, выклікаецца, што яны ўсе ў той жа час є рознымі адпаведзямі на адну дызайнерскую проблему: насколькі можна шырока аддаляць UI, прычаму фрамворк прыкладнення не павінен усюды прасачвацца ў кожную абстракцію?

Headless не тое ж самае, што framework-agnostic

Headless UI вже рашае значную частку проблемы. Radix Primitives — яскравы прыклад таго, чаму гэты падход стаў популярным. У замяне на выдачу элемента Dialog з кольорам фона, відстаням між елементамі, ценюючым эфектам і радыусам края, якія заданы кімось іншым, ён выдае складныя часткі коду, а выбор зовнішняго візуальнага відтворэння залишаецца за вам.

Этыя складныя аспекты — цэлая праца: кантроль увагі, навігацыя за дапамою клавіатуры, правильныя атрыбуты ARIA, поведанне пад час закрыцья, а таксама безліч дробных деталей, якія легка прыгнучы, калі модальны элемент застаецца проста div, які знаходзится на іншым div. Radix спецыяльна выдае своі прымітывы без стайлінгу і практыкуе ў якосты доступных прымітываў React. Вы кантролюеце візуальную прыемнасць, але базовая модэль застаецца React.

Гэта здаецца бяспадзейным, але ў гэтам є наследкі. Адменшэнне залежнасці ад стайлінгу не значыць адменшэнне залежнасці ад фреймворку. То ж самае верна і для Angular: прымітыв можа быть абсалютна нейтральны ў паводзе з кольорамі, відстанямі між элементамі і типаграфікай, пры тым як ён цалкам залежыць ад дырэктываў Angular, сігналаў, вводу залежнасцей і хуків жыцёвага циклу.

Это не недакладка. Частае ўсё-такі самэ гэта яшчэ і правы выбар. Прымітіва, створаная для Angular, можа жорсткая прыўязвацца да Angular, а прымітіва React можа выкарыстоўваць модэль складання React. Глыбока інтеграцыя зазвычай дае лепшы досвід разработкі, чым адказ на тое, што фрэймворк не існуе. Справа толькі ў тым, што два падходы часта спрыяюцца як сінонімы, хоць на самай працэ ўсё не так:

headless
   ≠
framework-agnostic

Компаненты без візуальнага элемента пасвячаныя толькі логіцы. Абстракцыі, незалежныя ад фрэймворка, заходзяць на ўжо адну ступень глыбей і намагаюцца пасвячыцца не просто логіцы, але і самам элементу відображэння. Це два разныя рывень перадачы коду, і важна знать, каторы з іх на самай працэ трэба.

Біявірство можа знаходзіцца пад самым компанентам

Узьмімо падаючы список. Два такія элементы можу цалком не нагадваць адно другаму з візуальной точкі зору. Адны знаходзится на старонцы маркетынгу з вялікім шрыфтом, достатнькай колькасцю працоўнага прастору і анімаванымі переходамі; іншы — у тыснучай паліцы інструментаў IDE, дзе кожны піксель мае значэнне. Адны з іх можа быць створаны за дапамою Angular, іншы — React, а трэці — веб-компонентам.

Пад візуальнымі разлікамі продовжваюць востанавляцца тыя ж запитанні:

  • Чы розкрываецца падаючы список зараз?
  • Калькі элемент ў актыўным стане?
  • Што робіць клавіша Escape?
  • Чы можа корыстнік пераводзіцься между элементамі за дапамою клавіш-стрэлак?
  • Як адносіцца да неактыўных элементаў?
  • Куды пераходзіць фокус пасля закрыцца падаючага списку?

Ніякі з эўтах запытанняў не залежыць ад таго, чы маркап быў створаны Angular чы React. Це перш за ўсё проблемы взаімадзеяння, а не проблемы рэндарування.

Машыны станоў як спакойны контракт

Што робіць, калі інтерфейс парадыгмай становых машын стае такім прываблівым. Zag.js — адзін з найвядомейшых прыкладаў такога падходу. Уместо таго, каб рассіцькаваць компонент React як аднаго ўсепраўднага джерела, Zag моделюе взаімадзею як машыны, незалежныя ад фрэймворку. Аддатчыкі фрэймворка пасля чаго налаштовуюць гэтыя машыны так, каб React, Vue, Solid, Svelte і іншыя моглі кераваць реактыўнасцю, жыцёвым циклам і DOM, а ў даследжэннях паказана, як напісаць аддатчык для фрэймворку, які ўсё ще не падтрымліваецца.

У звычайным компоненте кожная задача ўпаковываецца ў адну елементарную частку, спецыфічную для конкрэтнага фрэймворку:

React Dropdown
  ├─ state
  ├─ interactions
  ├─ accessibility
  └─ rendering

Калі машына знаходзіцца межы, поведэнне визначаецца аднойчы, а кожны рэндерар яго викорыстоўвае:

Dropdown behavior
                    │
               state machine
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
     React         Vue         Svelte

Апошнія компаненты застаюцься окремымі компанетамі, і кожная платформа застаецца такой жа платформай. Тое, што было перанесенае, — это контракт паведынкі, які ўжо болей корыстны для апісання платформ-незалежных рашэнняў, чым фантазія пра адной „чароднаяй“ компанете, якая працуе всюды без жадных работ з інтэграцыяй. Кращыя словы для апісання: запісаце паведынку адной раз, а потым прыстосаваце яе да таго рэндерара, які падходзіць для прыемлівання дадзеных.

Корыстным ментальным моделю ёсць тое, што машина — это чыстыя апісанні станоў, запускаючыхся дзеянняў і пераходаў межы станамі. Паколькі ёна не мае ні кансаўтаментных рэферэнсоў, ні станаў платформы, яе лёгка тэставаць ізольавана: вы падаеце ёй дзеянняі і пераканваецеся ў рэзультатным стане, не інсталюючы нічога.

Компанента можа існаваць як паведынка раней, чым стане часткай UI

Той жа ідея прыменяецца і ў бібліятэкі веб-компанентаў. Уявіце сабе бібліятэку, створаную за дапамою Stencil, якая містіць звычныя кантакты, дыалогавыя вікна, выпадаючыя спісы і карточкі. Гэтыя компаненты не патрабуюць замены на машыны станаў. У замен, пад імі можа знаходзіцца аддзельны шар.

Фабрыка кантактаў ведае пра станы «заблокаваны» і «загрузка», обробку кліканняў і пра тое, якія атрыбуты павінны быць прысвоены інтерактыўнаму элементу. Фабрыка выпадаючых спісоў ведае пра ўворачанне, закрыцце, выбіранне і навігацыю за дапамою клавіатуры. Фабрыка дыалогавых вікнаў ведае пра ўсё жыццёвы цикл і правіла взаімадзеяння. Ніякая з гэтых логік не павінна вяліць пра тое, як выглядае компанент.

Канцэптуальна, такія рашэнні можу існаваць ўжо до таго, як будзе прынятае рашэнне пра атрыбутаванне.

const button = createButton({
  disabled: false,
  loading: false,
  onClick(event) {
    // application behavior
  },
});

Зверніце ўвагу на тое, чаго не хапае: ні Stencil, ні Angular, ні компонента React, нават CSS няма. Фабрыка — це толькі апісанне поведзення, а рэндерар можа быць прыўязаны пазней. Напрыклад, кнопка Stencil з бібліятэкі выкарыстоўвае гэтая поведзенне і адкрывае стайлізаваны Custom Element:

<and-button variant="destructive">
  Delete
</and-button>

Іншая аплікацыя можа абгорнуць зусім іншую кнопку навакола таго ж самага поведэнческага ядра. Разглядзім IDE у стыле настольнага комп’ютера, чый інтэрфейс намеравана ў значныяй меры ўжорсткішы, чым у звычнай веб-сторонцы. Його кнопкі выкарыстоўваюць іншыя размеры, іншые дизайнарскія елементы та іншую візуальную мову, таму імпорт кнопкі Web Component загальнага назначэння быў бы неправым рашэннем. IDE проста мае свою сабецяю кнопку Angular, і гэтая кнопка Angular все равно можа вызваць тую ж фабрыку createButton().

Гэта і є практычны выгода. Мета не ў гэму:

ONE BUTTON
    ↓
use everywhere

Мета такая:

shared behavior
                     │
          ┌──────────┴──────────┐
          ↓                     ↓
   Web Component          Angular component
          ↓                     ↓
      Web UI                 IDE UI

Адаптаванне інтерфейса застаецца локальным для кожнага продукту, а сложная логіка взаімадзеяў не патрабуе перапісвы для кожнага з іх.

Web Components робяць компоненты, якія адрасуюцца, переноснымі

Машыны станоў робяць поведзенне переносным. Яны нічога не робяць з адрасаваным UI, і самэ гэта робіць Web Components цяжкавымі. Кастамны элемент, падобны таму, што нижэй, належыць да Web Platform, а не да Angular, React чыю Vue:

<and-button>
  Save
</and-button>

Angular можа яго адрасаваць, Astro можа яго выдаляць, React можа яго выкарыстоўваць, а звычная HTML-сторана можа яго уключыць за дапамою тагу script. Фрэймворк, які яго абгружае, можа змяніцца, пры тым як сам элемент застаецца абсолютна такім жа.

У практыцы падтрымкі фрэймворкаў для Custom Elements разніцяюцца за деталямі. Angular патрабуе CUSTOM_ELEMENTS_SCHEMA (או ўзаемна заменны элемент) каб прыняць невядомыя тэгі, а React історычна перадаваў значэння як атрыбуты, а не як власнасці, што ускладняла роботу з багатымі дадзеннямі і спецыяльнымі запускамі, хоць пасляўнія версіі ўжо паўрабілі це. Цікава будзе пераканацца ў тым, які падтрымкі Custom Elements мае ваш фрэймворк, прычымляючыся да гэтага падходу.

У канцы вы отрымваеце два разныя відэры рекалібрацыі. Аднам з іх ёсць переносная працэздатнасць:

State machine
    ↓
portable behavior

Другім ёсць переносны компонент, які быў выкарыстоўаны для адрасавання:

Web Component
    ↓
portable rendered component

Іноды вы хочаце цэлаг Компонента Вебу. Якщо элемент кнопкі ў системе дизайна должен выглядаць і працаваць аднакова ў калькі разоў у розных прыемплеях, яго пакаванне як Спецыяльнага Элемента ёсць разумным выборам. Іншыя разы вы вовсе не хочаце цэлаг компонента. Сценарый IDE — гэта самэ це: заставіць логіку взаімадзеяння, але дазволіць самам прыемплею цалком кераваць відрасункамі і дизайном.

Гэтыя не ўзаемна выклікаючыяся архітектуры; яны проста ставяць межы абстракціі ў разных месцах. Мышленье пра межы, а не пра бібліятэкі, таксама раз’ясняе ўсё іншае: не кожны рэусаблівы элемент UI павінен быць компонентам.

Лейаут не патрабуе компонента

Лейаут — гэта найяснейшы прыклад. Ёсць звычайны элемент Tailwind:

<div
  class="
    flex
    flex-col
    items-start
    gap-6
    p-6
    rounded-xl
    border
    bg-card
    shadow-sm
  "
>
  ...
</div>

Няма нічога паследнага. Адна з справжніх пераваг Tailwind — гэта можлівасць разумець большую частку элемента без павтарання між шаблонам і стылям. Але пагляньце, сколькі функцый выпало на долю тага class. Дзеяныя з класаў описваюць візуальную ідэнтычнасць:

rounded-xl
border
bg-card
shadow-sm

Іншыя класы описваюць прасторовыя адносіны межаў элемента і яго дзецячых элементаў:

flex
flex-col
items-start
gap-6
p-6

Браузер не робіць разліку межаў імі. class — гэта проста універсальны механізм для прыўязкі ідэнтыфікатораў, якія можна выбраць за дапамогою CSS і JavaScript; HTML не мае панявы пра „класы распакоўкі“ і „класы дизайну“. Аднак як рашэнне па проекцыі API, разлучэнне гэтых двух категорый выявляецца практычным. Эксперыментальны атрыбут and-layout выражае прасторовую частку окрема:

<div
  class="card"
  and-layout="vertical align:start gap:lg p:lg"
>
  ...
</div>

Прыткасць тут няма; інодзе такая версія нават не корачэ. Прыткасць у тым, што адпаведна адказальнасць стае відчутная: class описвае, як выглядае элемент, а and-layout — як ён распалюе прастор.

Як рэалізуецца атрыбут

Для рэалізацыі не патрэбны ні фрамворкі, ні JavaScript. Це чысты CSS: кожны элемент атрыбута падходзіць під селектары атрыбутаў (селектар ~= з роздзеляючымі прабеламі словамі ідеальна падходзіць) і пераводзіцца на правіла Flexbox, Grid, расстоянняў і адпаведнасці. Такая декларацыя, як у прыкладзе, ў сушчыні ёсць проста CSS Grid з пунктамі перерыву.

<div
  and-layout="grid cols:1 cols@md:2 cols@lg:3 gap:lg"
>

За ўсім гэтым няма жаднага механізма распалювання, няма дырэктываў Angular, няма компонентаў React, і няма такога обгортка, як гэты, чыму ўсё толькі якщо вы справа жадаеце структуры типу Stack:

<Stack direction="vertical" gap="lg">

Гэта характарыстыка мае значэнне. Компаненты для разметкі не ўсё такія паслабыя. Абстракцыя <Stack> можа быць вельмі зручной, ўсёлякш у рамках системы дизайну фрэймворку. Падход з атрыбутамі — проста ўсё тое ж самае альтернатыва: калі элемент, які вам трэба, вже існуе, можа не быць прычыны ствараць дадатковы компанент толькі для таго, каб описаць, як розмешчаны його дзеці.

Спакоў, які трэба памятаць: спецыяльныя атрыбуты без прыметніка data- не ўважаюцца валідным HTML за стандартам, нават якшо кожны браузер ўсё равно правільна іх стылюе. Калікаторы валіднасці та дзеякі інструменты для перагляду коду будуць скаржыцца, а правільная распісва data-and-layout пазбегае гэтага, але за рахунак незначнай додатковай довжыны.

class можа робіць усё, але не зобав’язана

За гэтым стоіць шырэйшы патэран. Савременныя маркапы можаць перакласты вельмі вялікую частку адпаведальнасці на class. Якщо дадаць CSS-функцыі та плагін для анімацый, то абсалютна нормальная элемента можа ператворыцца на такую:

<div
  class="
    card
    flex
    flex-col
    items-center
    gap-6
    p-8
    rounded-xl
    border
    bg-card
    animate-in
    fade-in
    slide-in-from-bottom-4
    duration-500
  "
>

Гэта працюе, і чытабельныя класы-функцыі набагато кращыя, чым выдумкі пра тое, што кожны інтэрфейс патрабуе ідеальная семантычная іерархія BEM. Асловны вопыт не ў тым, чы можа class несці весь гэты тыраж; яго, адзвічайна, можа. Вопыт у тым, чы должен адзін атрыбут выражаць усія можлівасці элемента. Раздзелэнне адпаведальнасцей дае вам:

<div
  class="card"
  and-layout="vertical align:center gap:lg p:xl"
  and-motion="slide-in-up"
  and-motion-duration="500ms"
>

Элемент тепер за адзін погляд паказвае тры разлучныя адпаведальнасці:

class       → visual identity
and-layout  → spatial organization
and-motion  → animation

Спакульвайце гэта як невялікае застосоўванне прынцыпа адной адпаведальнасці да маркапу. HTML гэтага не выкарыстоўвае. Мотывацыя заключаецца у тым, што рэзультат лёгкія для чытання, перагляду та змены.

Дзіяннее адпрацоўваецца чераз сабстоячы декларатывны канал

Сектор анімацый будуе на модэлі, якая ўжо багаты годы добра працуе. Animate.css, які выпускаецца на animate.style, керуе анімацыямі выключна за дапамой класаў: дадзіце базовы клас плюс назву анімацыі, і элемент будзе анімавацца.

<h1 class="animate__animated animate__bounce">
  Hello
</h1>

Это проста, знайома і практычна без зайвых формальнасцей. Плагіны анімацый, створаныя на базе Tailwind, следуюць той жа філасофіі, ператвараючы анімацыю на ўзаемна складовыя інструменты ў class.

Інтэресны вопыт тут не ў тым, як стварыць болей моцную систему анімацый. Гэта падобна да вопыту пра макет: якщо анімацыя — це окремая проблема, яна можа маты свой сабстоячы декларатывны месца ў маркапе. У замест на тое, каб размішваць інструменты анімацый празоўсюды:

<div
  class="
    card
    animate-in
    fade-in
    slide-in-from-bottom-4
    duration-500
  "
>

вы описваеце анімацыю окрема:

<div
  class="card"
  and-motion="slide-in-up"
  and-motion-duration="500ms"
>

Калі тригар падае на спад, яго неабяжна чыткая прызначэнне разам з часам адлегківання і трываласцю:

<div
  class="card"
  and-motion="slide-in-up"
  and-motion-trigger="enter"
  and-motion-duration="800ms"
  and-motion-delay="200ms"
>

Маркап выявляе, якая анімацыя запускаецца, калі і як ёй керуецца часам; рэалізацыя займаецца раштой. Для трыгара enter гэта, як правіло, означае викорыстанне IntersectionObserver для стежэння за элементам. Трыгары, заснованыя на парадку над элементам і ўдарэнні, викорыстоўваюць свае сабскрыпты. Чырвоны момент — запит пра prefers-reduced-motion можна вырашыць у аднам цэнтральным месца, замест таго каб кожны разрабітель компонента памятаў пра гэта.

Атрыбуты — не магія. Яны даюць можлівасць элементу аб’явіць свае можымасці без ўключэння іх у дрэво компонентаў фрэймворку.

Декларатыўны падход, пакуль ён дапамагае

У таког падходу є меры. Простая анімацыя вхіду ідеальна падходзіць для адного атрыбута:

and-motion="fade-in"

Пераход выйшчы з модальнага вікна — гэта іншы тип проблемы. Падазрóў, што модальна вікно павінна анімавацца і быць выдалена з DOM толькі пасля таго, як анімацыя завершыцца. Тады маю значэнне жыцёвы цикл і порядак дзеянняў, а простая імператыўная вызова ясней выражае намер:

await player.play('fade-zoom-out');
closeModal();

Прабаўка закодаваць кожныя аспекты жыцёвага циклу ў постаўляючыся растучы атрыбут HTML толькі пагаршыць декларатывны API, а не павысіць яго якасць. Кансэптуальная правіла — выбраць самы просты спосаб, які точна адражае проблему, незалежна ад таго, чым гэта будзе звычны CSS, атрыбут, машына станоў, звычная служба фрэймворку чы веб-компонент. Декларатывны стыль не павінен стаць догмай. Ніхто не намагаецца пазбавіцца JavaScript чы фрэймворкаў. Мета — утрымацься ад пераводзуць кожнай проблемы на самую складную абстракцыю, якая толькі існуе, толькі таму, што яна ў наявнасці.

Атрыбуты як маленькія API для функцый

Калі як макет, так і рух будуць следаваць гэтам патэрану, атрыбуты начнуць выглядаць не так як элементы налашчэння, а больш как маленькія API для задання функцыянальнасці. Паглядзіце на гэты фрагмент:

<section
  class="feature-card"
  and-layout="vertical gap:lg p:xl"
  and-motion="fade-in"
  and-motion-trigger="enter"
>
  <h2>Framework-agnostic UI</h2>
  <p>At least as much as possible.</p>
</section>

Это все ўсё <section>, і його сэмантичны значэнне застаецца незменным. Чырагледзенне, візуальная прастота та анімазія падчас з’яўлення не выклікалі патрэбы ў викорыстоўванні такога шару обгорак:

<and-stack>
  <and-motion-container>
    <and-card>
      ...
    </and-card>
  </and-motion-container>
</and-stack>

Тая вярсія з вкладанням не ўсега ўлучна. Якщо гэтыя элементы маюць значныя функціі і надаюць корыстную API, яны цалкам можуць стаць компанентамі. Разлік ў тым, што перадаўальная можлівасць не павінна автаматычна стаць ўсё новым компанентам. Часта вузел DOM уже існуе і яму проста патрэбны форматаванне, рух або функція падказкі. Фрэймворк не завжды павінен пра гэта ведаць, а калі абстракцыя знаходзится безпосередньа на HTML, яна можа бесплатна быць викорыстаная ў Angular, Astro, React, Vue або на старонцы без фрэймворка.

Фрэймворк-незалежнасць не значыць відсутнасць фрэймворка

Хутчэй заўважыма рызыкам ёсць тое, што падход «незалежны ад фрэймворка» можа лёгка ператварыцца ў ўжо адну з супербораў чыстасці, што цэлкам паспяшыць за мету. Фрэймворкі здобываюць своё месца праз інтэграцыю разных элементаў. Angular апраняе сігналы, шаблоны, ввод залежнасцяў, формы, маршрутызацію і чыстую модэль аплікацыі. React распаўзае вельмі развіты экасыстэм композіцыі. Vue і Svelte праставляюць іншыя компромісы.

Паведанне, незалежнае ад фрэймворка, як і раней, павінна быць прыўязанае да рэактывной системы кожнай аплікацыі. Самэ гэтая прычына, чаму Zag выдае адаптары фрэймворкаў: адаптар прыўязывае машыну да рэактывнасці, жыцёвага циклу і стандартаў DOM фрэймворка, і машына можа застацца незалежной толькі тады, калі іншы слой разумее фрэймворк.

Метод паверховасцяў, описаны тут, таксама неякш.

  • Компанента Angular, створаная на абстрактнай фабрыцы стану, можа выклікваць патрэбнасць у effect(), каб сигналы, якія прыходзяць, заставаліся сінхроннымі з зовнішнім станам.
  • Web Component павінен з’яеднаць шар стану зі своімі власнымі функцыямі, якія выконваюцца працэю жыццўнага циклу.
  • Лейаут, які базуецца на атрыбутах, вводзіць невялікі DSL, які кожны член команды павінен выучыць.
  • Атрыбуты, якія керуюць рухам, дадаюць ўсё новыя можлівасці API.
  • Раздзелэнне всього на окалечныя пакеты стварае кантракты, якія павінны заставаліся сумеснымі з часам.

Інодзе просты компанент, створанный спецыяльна для певнай платформы, ўсё-такі являецца кращым рашэнням з точкі зору дизайна. Самэй тыя комплекты компанентаў, прызначанныя толькі для Angular і якія можна проста скопіюваць, заступныя ўсё ж маюць сэнс пад час роботы з усім гэтым. Ніхто не должен праходзіць кожны кнопкі Angular через ланцуг з чатырох адаптараў, пару машын станоў і Web Component, толькі таму, што падачка «агнэстычны» звучыць болей сложна. Цэе дужа дорогі спосаб прыменшыць неабходнасць напісання:

<volt-button>

Незалежнасць ад платформы ўзнаграджваецца, калі переноснасць ёсць рэальныяя вымога. Калі ж гэта не так, часта кращым рашэнням ёсць жорсткая інтеграцыя з платформай.

Інтерфейс, незалежны ад платформы, як стак, а не бібліятэка

Фраза „бібліятэка UI, незалежная ад фрэймворку“ зазвычай нагадвае працоўную групу компанентав, якія яким-небудзь спосабам працуюць усюды. Web Components даскладна набліжаюцца да гэтага для рэндаруемых компанентав. Аднак болей корыстным модэлем ёсць не адна універсальная бібліятэка, а набор адпаведнасцей, калі кожная з яых стае спецыфічной для певнага фрэймворку ў разны момант:

Application
                         │
          Angular / React / Vue / Astro
                         │
                  framework adapters
                         │
    ─────────────────────────────────────────
                         │
         headless behavior / state machines
                         │
     Web Components     HTML capabilities
          │                 │
          │          layout / motion attributes
          │                 │
    ─────────────────────────────────────────
                         │
                    Web Platform

Някакая аплікацыя не павінна викорыстоўваць кожны слой:

  • Адзін проект викорыстоўвае Web Component безпосередна і ніколі не чапаеся за стан без галавы, який знаходзіцца пад ёю.
  • Іншы викорыстоўвае толькі машыну стана і будуе свою сабэйскую Angular UI зверху.
  • Статычная сторонка Astro можа не патрабаваць нічога кроме атрыбутаў макету і руху.
  • Аплікацыя Angular можа ігнораваць весь набор і викорыстоўваць кіт, створаны спецыяльна для Angular, з прымітівамі Angular, адколі гэта дае найблагаўшы досвід разработчыка.

Сама ідея — гэта можлівасць сумешчання разных элементаў. Тэрмін «незалежны ад фреймворку» не значыць, што фреймворк забранены. Гэта значыць, што вы самі вярнуецеся за тым, дзе будзе межа фреймворку, а не прабачаеце яму автаматычна распространяцца па кожным відновімаемым слою.

Заліквідацыя фреймворку ў слое аплікацыі

Усё гэта не ўзгляд проты Angular чы якога-небудзь іншага фреймворку. Цэў узгляд на тое, якую відпаведальнасць фреймворак прыймае за замовчаннем. Разглядзіце прыклады знову з гэтай точкі зору:

  • Візуальны дизайн выпадаючага спісу можа належаць продукту, а яго відрасункаванне — Angular, але ўзаўмовыніканне з корыстувальнікам не павінны такога выкарыстоўваць.
  • Спяльны кнопкі можа быць Web Component, калі вам трэба той самы кнопкі ў калькольных проектах, або спецыяльны Angular-компонент, який дзеліць толькі невялікі модуль поведзення, калі продукт патрабуе свой саўстанні візуальны стыл.
  • Карточку можна сформатаваць за дапамою Tailwind, а ў той жытак зберагчы ўсю структуру разметкі ва and-layout.
  • Для стварэння эфекту пры ап’яве можа патрабавацца толькі невеликі абсарбер і атрыбут, пры чым не мае значэння, чыя саме фреймворкі (Astro чы Angular) стварылі HTML.
  • Якща розглядаць усе разам, то всі элементы складаюця цэласць:

    • Headless UI адключае візуальныя элементы.
    • Машыны станоў зваліваюць поведзенне з адносу да конкрэтнага рэндерара.
    • Web Components дазволяюць готовым, рэндараваным компонентам пераходзіць межу разных стэкаў.
    • Спецыяльныя атрыбуты прыкрепляюць лёгкія функцыяналі, такія як структура разметкі і анімацыя, безпосередна да самай HTML-разметкі, а не да фреймворка.

    Ніякі з эых ідэй не ўсё новыя, і не кожную систему UI трэба ствараць такім спосабам. Аднак у сукупнасці яны крытыкуюць давно існуючы стандарт — што фрэймворк таксама павінен знаходзіцца пад кожной перאбстракцыяй UI, якае можа быць викорыстана ў прыкладнай програме.

    Ключовыя выводы

    • Разлічваць "без стылю" і "незалежны ад рэндара"; большасць бібліятак без галоўнага вікна ёсць толькі першыя.
    • Логіку взаімадзею, якую выкарыстоўваюць калькі продуктав, размешчаць у машынах стану або фабрыках без фрэймворка, і свядома прыйміць выклікі, якія це стварае.
    • Выкарыстоўваць Web Components, калі сам компонент, які рэндаруецца, а не толькі його прыемы, павінен быць ідэнтычным у разных сістэмах.
    • Для макетавання і простых анімацый выкарыстоўваць толькі атрыбуты CSS, а калі стане важлівым порядак жыццўага циклу — перайсці на імператыўны код.
  • Зберагаюце компаненты, спецыязавучыя для праметры, там, дзе пераноснасць не ўскладненая; мета — выкарыстоўваць праметры без таго, каб кожны слой залежаў ад іх.
  • Спадні матэрыялы