Адзін запит Todo, две архітектуры: што htmx выкарачоўвае з стака CRUD
Адаптавайце адзін запит на дадзенне элемента ў React SPA і сторанцы htmx, каб пазначыць, якія слоі зникаюць, што перэймае сервер, і дзе htmx больш не можа функцыонаваць.
Крыніцы: Stop Over-Engineering CRUD Apps: Why HTMX Might Be All You Need
Большасць внутрашняях інструментаў і панелей адказчыка выконваюць тыя ж чатыры задачы: стварэнне, чытанне, апдэйт і выдаленне записаў. Аднак стандартны спосаб іх стварэння ператворыўся на цэлую аплікацыю на одной сторонце, якая складаецца з фрэймворку компанентав, менеджера стану, бібліятэкі для запошчання дадзеных, канцэнтраў JSON і коду кліента, главная задача якога — пераканваты гэты JSON знову ў HTML. У які-то момент форма, якая дадае адну строчку ў базе дадзеных, становіцца залежной ад паўсотні пакетаў і двух інструментаў для стварэння.
У этайнай стацыі разглядваецца адна конкрэтная функцыя – дадзенне элемента ў спіс задач, пры чым паказаны яе рэалізаванні ў React і htmx. Працава з цыклам запыткаў паўнаста дазволяе лёгка адначасова ацэніць, якія элементы на самай працоўцы вашага CRUD-прыстрою дасканальна неабходны, і калі заўсёды правільным выборам будзе болей моцная кліентская фрамворк. Для болей шырокага паўстанавлення hypermedia проты стандартнага SPA, адзірніце htmx проты стандартнага SPA.
Трыбуты, якія на самай працоўцы маюць большасць CRUD-прыстроў
Спроставім тыповыя бізнес-даплікаты да таго, што робяць іх корыстувальнікі. Яны заходзяць у систему, ствараюць записы, рэдагуюць іх і выкарыстоўваюць. Зазвычай няма режыму без падключэння да інтарнету, няма можлівасці рэальнага часу для саавтарства, і няма такой шыранай взаімадзеі, якая вымагала б обработкі без перавыходу да сервера. Нічога з гэтага не ўзначае патрэбу викорыстоўвання віртуальнага DOM, маршрутацыі на кліянтзе чы і чакання большай часткі хвілі, пакуль інструменты перзбудуюць адны кантакт.
Фрэймворкі часта выбіраюцься з прыzwычкі, яшчэ перш чым хтось запісае, што павінен робіць даплікат. Пачатак з тэрэтарыў зазвычай прыводзіць да значна меньшага набору інструментаў.
Як htmx зменяе угоду з серверам
htmx дазволяе звычайным элементам HTML выканаць запиты HTTP чераз атрыбуты. Сервер адпавядае фрагментам HTML, а не JSON, і htmx вставляе гэты фрагмент у стораніцу ў пазначаны вам месца.
Паслягодзею ў тым, што не існуе копіі дадзэнняў на стороне кліента, якую можна было б кераваць. Сэрвер вялікі час ведае, як павінна выглядаць спіс, таму ён генеруе готавы маркап і адправляе яго. Браузер проста должен разместіць яго на правым месцы.
Тая ж функцыя ў обох стылях
Версія на React
Ёсць стандартны компонент на React, який дадзеўвае элементы до спісу задач. Ён зберагае текст вводу і спіс у стане, адправляе JSON да API, парасканавляе адпаведзь, дадзеўвае новы элемент і чыстае поле.
function AddTodo() {
const [text, setText] = useState("")
const [todos, setTodos] = useState([])
const addTodo = async () => {
const res = await fetch("/api/todos", {
method: "POST",
body: JSON.stringify({ text }),
})
const todo = await res.json()
setTodos([...todos, todo])
setText("")
}
return (
<div>
<input value={text} onChange={e => setText(e.target.value)} />
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(t => <li key={t.id}>{t.text}</li>)}
</ul>
</div>
)
}
Нават гэты маленькі прыклад мае деталі, якія неабходна правільна адрабатваць. Запрос надае тэла у формате JSON без заголовка Content-Type, таму багато сервераў не будуць парсаваць яго як JSON. Функцыя setTodos([...todos, todo]) чытае значэння todos з тага, дзе адбылася клікання, таму два швыдкіх запуску можу прывести да втраты элемента; функцыя у формате functional setTodos(prev => [...prev, todo]) запобегае гэтаму. Кроме таго, спіс містыць толькі элементы, даданыя ў цій сесіі, адтак як для завантажэння існуючых элементаў патрэбны ўжо іншыя эфекты та канцэнтры.
Версія htmx
Эквівалент htmx — это форма і порожній спіс.
<form hx-post="/todos" hx-target="#list" hx-swap="beforeend">
<input name="text" />
<button>Add</button>
</form>
<ul id="list"></ul>
Тры атрыбуты каналізуюць гэтая поведенне. hx-post="/todos" адправляе поля формы да гэтага URL, калі форма адправляецца. hx-target="#list" паказывае, куды пайшоў адпаведны рэспонс, а hx-swap="beforeend" дадае яго ўнутрь таго элемента, пасля існуючых дзецячых елементаў, замест таго, каб заменіць яго ўместа. Кнапкі не патрабуецца адпаведны працоўнік, таму што кнапка ўнутрь формы за значэнням адправляе яе.
На серверы маршрут чытае поля формы, зберагае задачу і адрасавае адну строчку спіса з шаблона.
@app.route("/todos", methods=["POST"])
def add_todo():
text = request.form["text"]
todo = save_todo(text)
return render_template("todo_row.html", todo=todo)
Это ўсё фронтэнд для гэтай функцыянальнасці: няма хуків, няма стану кліента і няма прыемлів перыскаладзення для дэбагавання паўнічы. Шаблон todo_row.html проста атрыбутуе адзін <li> для новага элемента. Паколькі той самы шаблон можа быць выкарыстоўваны пад час першага атрыбутавання сторункі, пачатковы список і новададзеныя рядкі дзелюцца аднам куском маркапа, а не адной JSX-компанентай і аднам серверскім відображэнням, якія павінны заставацца сінхроннымі.
Для рэальных развяжанняў існуе калекцыя практычных прыказаў. Запрос, які зменяе стан, все рава патрэбуе захавання ад CSRF, зазвычай па ўключэнні токена як спрывучага поля або заголовка запросу. А якщо вы таксама дастанеце форме звычныя атрыбуты action і method, яна будзе працаваць як звычная адправка формы, калі JavaScript не можа быць заванесены.
Аналіз двух жыццёвых циклаў запросаў
Разлік стае яшчэ болей зрозумелым, калі вы практычна працуеце з кожным крокам прабегу. Спачатку — маршрут SPA.
Browser Server
-------- --------
Click button
|
v
JS event handler
|
v
fetch() call -------> API endpoint
|
v
Query database
|
v
Return JSON
^ |
|__________________________|
|
v
Parse JSON
|
v
Update state
|
v
Re-render component
|
v
Diff virtual DOM
|
v
Patch real DOM
Пасля таго, як база дадзеных вяртае JSON, кліент усё рава должен яго аналізаваць, адкорэгаваць стан, практычна перзапрацоўваць інтэрфейс, вылучаць разніцы та апдэйтаваць DOM.
Тепер — маршрут htmx для таго ж клікання.
Browser Server
-------- --------
Click button
|
v
htmx request -------> Route handler
|
v
Query database
|
v
Render HTML piece
^ |
|__________________________|
|
v
Swap HTML into page
Кальколька этапаў проста зникае: аналіз JSON на стороне кліента, адкорэгавання стану, вылучэнне разніцы, а таксама всі спробы падтрымкі супараднасці копіі дадзеных на стороне кліента. База дадзеных, якая знаходзіцца на сервере, завжды волела гэтыя дадзеныя. Падход htmx не выконвае падтрымкі другой, кліентскай версіі гэтых дадзеных.
Што змініцца на практыцы
У адміністрацыяй однага кліентскага проекту пад час міграцыі замена фронтэнда на React на шаблоны, вырабатваны на сервере, плюс htmx скармаліла розмер пакета JavaScript з прыблізна 340 КБ да апошней 12 КБ, тады як час стварэння інтэрактыўнасці на телефоне середней класы зменіўся з майже трохо секунд да менейш чым адной секунды. Колькасць просьбаў пра перагляд зменілася, таму што было менейш коду для аналізу, а новыя разработчыкі моглі розбірацца ў проекте за адзін дзень, адтуды што база коду складалася пераважна з HTML з калькама дапаможных атрыбутаў.
Спышчайце гэтыя цифры як ілюстрацыю, а не як стандартную вялічыну. Яны паходзяць з аднаго проекту, які быў неформальна перакананы на аднай машыне і ў адной сеті. Напрамак эфекту ёсць правдопадобным для будзь-ягоя прыкладнага програмістыка, дзе большая частка кліентскага коду служыць толькі для перадачы даных между JSON і DOM, але замерайце сабея прыкладнага програмістыка до і пасля, а не прыпускайце, што будуць тыя жыццёвыя выгоды.
Калі htmx — не падходячы інструмент
Это не аргумент проты ўсунення React з усіх практыкаў. Аплікэцыі з сапраўдна ўсёзначнымі вярхнімі ваздзейсненнямі, такія як інструменты дизайна, табліцы або рэальнага часу редактары для саўместной працы, патрабуюць значныя данні кліента і фрэймворк, створаны для ўправління імі. Прыведзенне іх да ваздзейсненняў на адной заявцы і замены будзе сталай борбай.
Шырокі спосаб прыняць рашэнне — задаць калькі пытанняў пра функцыю, якая стоіць перед вамі:
- Чы робота патрабуе адпаведзення быстрей, чым за час двух путаў паказніка, напрыклад падчас ператасоввання, адрамбавання чы напісання з мгновенным адпаведзеннем?
- Чы яна павінна працаваць без інтэрнету, чы супрацоўваць з правкамі кольках корыстнікаў у рэальны час?
- Чы кліент патрабуе зберагаць і пераробляць великі об’ём дадзеных локальна?
Якщо адзінкі большасцю ўтвараюцься з «нет», то такія функцыі, як формы, табліцы і кнопкі для зберагчэння дадзеных у базе, як правіло, ўключаюцься. Шырока категорыя такога програмнага забезпечэння перакрывае гораздзейшы спектар рашэнняў, ніж прыемна згадваць, і для яе додатковыя кліентскія механізмы часта толькі падвышаюць вартасць адтрымання, замест таго каб рашыць проблему.
Узагальненне
- Выбірайце інструменты па вашымі трэбаванням; простыя системы CRUD рэдка калі патрабуюць кліентскага стану.
- За дапамогою htmx сервер атрыбутуе фрагменты, і саме атрыбуты вялікія за тое, дзе яны будуць прадставлены, чым усунуліся патрэбныя пераклады ў формате JSON і дублірацыя аднаго і тога ж источніка дадзеных.
- Зберагачыце захіст ад CSRF, справжнія атрыбуты
actionі спакульнаныя шаблоны рядкаў. - Зарезерваваце цэлы кліентскі фреймворк для справжняя актыўнасці, якая выключваецца кліентам.
Выкліканне навыка работы з React застаецца розумным кар’ерным выборам, і багато проектаў яго патрабуюць. Гэта правяжа неабходнасцю ведаць гэты інструмент; аднак гэта не правяжа ўключэння яго ў кожны дапыт, для якога фактычныя патрэбнасці — це толькі форма і таблыця.