HappyX vs Karax: что проще?

от автора

Хотите узнать, как с помощью Nim создавать клиентские веб-приложения?

В этой статье я расскажу вам о веб-фреймворках в Nim и их возможностях, а также приведу примеры в сравнении друг с другом и другими фреймворками.

Обложка веб-фреймворка HappyX

Обложка веб-фреймворка HappyX
Обложка Karax

Обложка Karax

Начну с того, что в экосистеме Nim есть популярный веб-фреймворк, название которому Karax. Автором является Araq, по совместительству создатель Nim.

Andreas Rumpf

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.

<!DOCTYPE html> <html>   <head>   </head>   <body>     <div>       <p id="counter">         0       </p>       <button onclick="increase()">         Нажми на меня!       </button>     </div>     <script>       let count = 0;       const counter = document.getElementById("counter");        function increase() {         count++;         counter.innerHTML = `${count}`;       }     </script>   </body> </html>

Пример выше, написанный на 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:

<template>   <div>     <!-- Conditional Rendering: отображение заголовка в зависимости от значения showTitle -->     <h1 v-if="showTitle">Добро пожаловать!</h1>     <h1 v-else>Скрытый заголовок</h1>      <!-- List Rendering: отображение списка пользователей -->     <ul>       <!-- Используем директиву v-for для перебора элементов в списке -->       <li v-for="(user, index) in userList" :key="index">         <!-- Отображаем информацию о каждом пользователе -->         {{ user.name }} - {{ user.email }}       </li>     </ul>   </div> </template>  <script> export default {   data() {     return {       // Значение для условного отображения заголовка       showTitle: true,       // Список пользователей для отображения       userList: [         { name: 'Иван', email: 'ivan@example.com' },         { name: 'Мария', email: 'maria@example.com' },         { name: 'Александр', email: 'alex@example.com' }       ]     };   } }; </script>

В примере выше описан небольшой пример использования условного и цикличного рендеринга.

Как же это выглядит в 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 сразу приведу пример объявления компонента и его использование:

<template>   <!-- Объявляем наш компонент Button -->   <button @click="handleClick">{{ buttonText }}</button> </template>  <script> export default {   props: {     // Пропс для передачи текста кнопки     buttonText: {       type: String,       required: true     }   },   methods: {     handleClick() {       // Метод, который будет вызван при клике на кнопку       console.log('Клик по кнопке!');     }   } }; </script>
<template>   <div>     <!-- Используем компонент Button и передаем текст кнопки через пропс -->     <Button buttonText="Нажми меня" />   </div> </template>  <script> // Импортируем компонент Button import Button from './Button.vue';  export default {   components: {     // Регистрируем компонент Button для использования в текущем компоненте     Button   } }; </script>

Таким образом, использование компонентов значительно облегчает разработку одностраничных приложений. Однако как пример выше выглядит в 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.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Что бы вы стали использовать в разработке веб-приложений на Nim?

20% Karax5
12% HappyX3
76% Посмотреть результаты опроса19

Проголосовали 25 пользователей. Воздержались 7 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/778058/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *