HappyX vs Karax: что проще?
Хотите узнать, как с помощью Nim создавать клиентские веб-приложения?
В этой статье я расскажу вам о веб-фреймворках в Nim и их возможностях, а также приведу примеры в сравнении друг с другом и другими фреймворками.
Обложка веб-фреймворка HappyX
Обложка Karax
Начну с того, что в экосистеме Nim есть популярный веб-фреймворк, название которому Karax. Автором является Araq, по совместительству создатель Nim.
![47530c17beae00abe0850be77842d9cf.png](https://habrastorage.org/r/w1560/getpro/habr/upload_files/475/30c/17b/47530c17beae00abe0850be77842d9cf.png)
Araq, создатель Nim
Обычное HelloWorld приложение выглядит следующим образом:
include karax / prelude
proc createDom(): VNode =
result = buildHtml(tdiv):
text "Hello, world!"
setRenderer createDom
Импортируем сам фреймворк, создаем функцию, возвращающую виртуальное DOM дерево и задаем эту функцию для рендера.
Как можно видеть, подход к разработке здесь декларативен, в отличие от большинства веб-фреймворков.
Возвращаясь к теме статьи — в экосистеме Nim с недавних пор появилась альтернатива Karax — HappyX. Основным преимуществом HappyX перед Karax можно назвать макро-ориентированность. это означает, что бо́льшая часть задач выполняется во время компиляции, тем самым увеличивая производительность конечного веб-приложения.
Сравним HelloWorld приложение, написанное на HappyX с тем, что выше:
import happyx
appRoutes "app":
"/":
"Hello, world!"
Подход к разработке здесь также в основном декларативен, однако в следующей статье я расскажу об императивном подходе к разработке в HappyX.
Обработка событий
В каждом клиентском веб-приложении присутствует обработка событий, например нажатие на кнопку, заполнение формы и так далее. Как же обработка событий выглядит в Karax и HappyX?
За основу я возьму следующую HTML страничку с кнопкой, при нажатии на которую счетчик просто увеличивается на 1.
0
Пример выше, написанный на Karax выглядит следующим образом:
include karax / prelude
var count = 0
proc createDom(): VNode =
result = buildHtml(tdiv):
p:
text $count
button:
text "Нажми на меня!"
proc onclick(ev: Event; n: VNode) =
count += 1
setRenderer createDom
Здесь мы импортируем фреймворк, создаем переменную
count
, далее создаем функцию, возвращающую VDOM и повторяем структуру HTML странички выше. Также на 11-й строчке видно объявление обработчика нажатия, в котором происходит увеличение переменнойcount
.
И наконец, пример, написанный на HappyX:
import happyx
var count = remember 0
appRoutes "app":
"/":
tDiv:
tP: {count}
tButton:
"Нажми на меня!"
@click:
count += 1
Здесь вновь идет импорт, далее создание реактивной переменной с помощью функции
remember
, после чего объявляется само приложение со структурой, описанной в HTML файле. Здесь также присутствует объявление обработчика нажатия на 11-й строчке, в котором происходит увеличениеcount
.
Динамичный контент
В одностраничных приложениях контент зачастую подгружается динамически. Например во Vue.js присутствуют директивы v-if
, v-else-if
, v-else
, а также директива v-for
для отрисовки списков, как написано в документации.
И так, рассмотрим следующий пример, написанный на Vue.js:
Добро пожаловать!
Скрытый заголовок
-
{{ user.name }} - {{ user.email }}
В примере выше описан небольшой пример использования условного и цикличного рендеринга.
Как же это выглядит в Karax?
include karax / prelude
import strformat, json
var showTitle = true
# Список пользователей для отображения
var userList = %*[
{ "name": "Иван", "email": "ivan@example.com" },
{ "name": "Мария", "email": "maria@example.com" },
{ "name": "Александр", "email": "alex@example.com" }
]
proc createDom(): VNode =
result = buildHtml(tdiv):
h1:
# Conditional Rendering: отображение заголовка в зависимости от значения showTitle
if showTitle:
text "Добро пожаловать!"
else:
text "Скрытый заголовок"
ul:
# List Rendering: отображение списка пользователей
for user in userList.items:
# Отображаем информацию о каждом пользователе
li:
text fmt"""{user["name"]} - {user["email"]}"""
setRenderer createDom
А вот так это выглядит в HappyX:
import happyx, strformat, json
var showTitle = remember true
# Список пользователей для отображения
var userList = remember %*[
{ "name": "Иван", "email": "ivan@example.com" },
{ "name": "Мария", "email": "maria@example.com" },
{ "name": "Александр", "email": "alex@example.com" }
]
appRoutes "app":
"/":
tH1:
# Conditional Rendering: отображение заголовка в зависимости от значения showTitle
if showTitle:
"Добро пожаловать!"
else:
"Скрытый заголовок"
tUl:
# List Rendering: отображение списка пользователей
for user in userList.items:
# Отображаем информацию о каждом пользователе
tLi:
{fmt"""{user["name"]} - {user["email"]}"""}
В обоих примерах на Nim можно изменять переменные showTitle
и userList
, а страничка будет автоматически обновляться, учитывая изменения переменных.
Помимо if-elif-else и for HappyX также поддерживает такие выражения, как case-of, when и while.
Отличие
when
отif
в Nim заключается в том, что первый выполняется во время компиляции, играя некую роль директив препроцессора#ifdef
или#if
изC++
, а второй выполняется как во время компиляции, так и во время выполнения.
Компонентный Подход
В разработке большинства одностраничных приложений используется компонентный подход, дабы вести разработку более эффективно, имея при этом красивую архитектуру проекта.
Возвращаясь к Vue.js сразу приведу пример объявления компонента и его использование:
Таким образом, использование компонентов значительно облегчает разработку одностраничных приложений. Однако как пример выше выглядит в Karax и HappyX?
Karax не предоставляет каких-то особенных инструментов для компонентного подхода, однако пример выше можно реализовать при помощи второй функции, которая возвращает VDOM кнопки:
include karax / prelude
proc renderButton(buttonText: string): VNode =
result = buildHtml(tdiv):
button:
text buttonText
proc onclick(ev: Event; n: VNode) =
echo "Клик по кнопке!"
proc createDom(): VNode =
result = buildHtml(tdiv):
renderButton("Нажми на меня")
setRenderer createDom
HappyX же, в свою очередь, предоставляет компонентный подход и пример выше будет выглядеть уже следующим образом:
import happyx
component Button:
buttonText: string
`template`:
tButton:
{self.buttonText}
@click:
echo "Клик по кнопке!"
appRoutes "app":
"/":
Button("Нажми на меня")
При этом HappyX не ограничивает разработчика таким подходом, поэтому при желании можно реализовать этот пример при помощи функции, вместо компонента.
На этом у меня все, как я писал выше — в следующей статье расскажу про императивный подход к разработке в HappyX.