HappyX в мире дикого фронтенда

от автора

Как обстоят дела у HappyX, а в среде фронтенд-разработки сейчас?

Я расскажу вам об этом в полной статье.

Вспоминая о Nim

HappyX — веб фреймворк, написанный на языке Nim. Благодаря возможностям языка мы можем компилировать наше приложение как в JS, так и в C. Таким образом HappyX поддерживает и frontend и backend, впрочем, я здесь для того, чтобы поведать новости разработки HappyX.

HappyX и функциональные компоненты

В одной из предыдущих статей, в которых рассказывалось о веб-фреймворках Karax и HappyX, я показал, что Karax использует функции для создания VDOM:

include karax / prelude  proc createDom(): VNode =   result = buildHtml(tdiv):     text "Hello, world!"  setRenderer createDom

В то же время я упомянул HappyX компоненты. Однако до последнего времени для создания даже самой простой кнопки приходилось терять в производительности. За занавесом банальная кнопка расширялась до нескольких десятков строчек кода. Так происходило из-за того, что компоненты предоставляли ООП возможности, в том числе использование методов и свойств, наследование и прочие довольно тяжелые и порой ненужные вещи. Давайте вспомним один из примеров из статьи про сравнение HappyX и Karax:

import happyx   component Button:   buttonText: string   html:     tButton:       {self.buttonText}       @click:         echo "Клик по кнопке!"   appRoutes "app":   "/":     Button("Нажми на меня")

В этом примере мы можем видеть свойство buttonText, а также его использование как текста для кнопки. Ниже, на последней строке находится использование компонента. Может показаться, что все довольно-таки просто и однозначно, но нет. Вот, какая функция одного только создания кнопки получается после компиляции в JS:

function initButton_536871130(uniqCompId_536871131, buttonText_536871132) {   function HEX3Aanonymous_536871156(self_536871157, ev_536871158) {}   function HEX3Aanonymous_536871159(self_536871160, ev_536871161) {}   function HEX3Aanonymous_536871162(self_536871163, ev_536871164) {}   function HEX3Aanonymous_536871165(self_536871166, ev_536871167) {}   function HEX3Aanonymous_536871168(self_536871169, ev_536871170) {}   function HEX3Aanonymous_536871171(self_536871172, ev_536871173) {}   function HEX3Aanonymous_536871174(self_536871175, ev_536871176) {}   var result_536871133 = null;   BeforeRet: {     var self_536871155 = {uniqCompId: nimCopy(null, uniqCompId_536871131, NTI33554449), buttonText: remember_536871134(buttonText_536871132), m_type: NTI536870994, isCreated: false, slot: null, slotData: null, created: null, exited: null, rendered: null, pageHide: null, pageShow: null, beforeUpdated: null, updated: null};     self_536871155.beforeUpdated = HEX3Aanonymous_536871156;     self_536871155.pageShow = HEX3Aanonymous_536871159;     self_536871155.pageHide = HEX3Aanonymous_536871162;     self_536871155.rendered = HEX3Aanonymous_536871165;     self_536871155.exited = HEX3Aanonymous_536871168;     self_536871155.created = HEX3Aanonymous_536871171;     self_536871155.updated = HEX3Aanonymous_536871174;     createdComponentsList_1946158656[0].push(self_536871155);;     result_536871133 = self_536871155;     break BeforeRet;   };   return result_536871133; }

И это еще без учета отрисовки и объявления самой кнопки! Именно тут и приходят на помощь функциональные компоненты:

import happyx   proc Button*(buttonText: string): TagRef =   buildHtml:     tButton:       {buttonText}       @click:         echo "Клик по кнопке!"   appRoutes "app":   "/":     Button("Нажми на меня") 

Как видите, поменялось только объявление компонента. Его использование никак не поменялось. И вот, что мы получили после компиляции:

function Button_536870914(buttonText_536870915) {   var result_536870916 = null;   var __el0_536871126 = initTag_1996489074([98,117,116,116,111,110], [initTag_1996489108(buttonText_536870915, true, [], false)], false);   __el0_536871126.addEventListener('click', (event) => {     Label1: {       var ev_536871127 = null;       ev_536871127 = event;       rawEcho([208,154,208,187,208,184,208,186,32,208,191,208,190,32,208,186,208,189,208,190,208,191,208,186,208,181,33]);     };   });   result_536870916 = initTag_1996489074([100,105,118], [__el0_536871126], true);   return result_536870916; }

И это все! Таким образом мы получили и прирост в производительности и уменьшение кода на выходе.

Из возможностей обычных компонентов функциональные компоненты сохранили аргументы и слоты. В примере ниже показано, как можно передавать HTML в слот функционального компонента, используя при этом аргументы:

import happyx   proc Button*(x: int, stmt: TagRef): TagRef =   buildHtml:     tButton:       stmt       tDiv:         "x is {x}"       @click:         echo "Клик по кнопке!"   appRoutes "app":   "/":     Button(100):       "Нажми на меня"

Будет интересно узнать, что вы думаете по поводу функциональных компонентов.

HappyX Native

HappyX также обзавелся собственной небольшой библиотекой для компиляции в натив (на десктопах используется браузер по умолчанию, на Android используется WebView, на iOS пока нет возможности компиляции).

Работает оно следующим образом — в корне проекта создается основной серверный файл, который служит для взаимодействия устройства и приложения. Выглядит он следующим образом:

import happyx_native  callback:   # HappyX Native helloWorld callback   proc helloWorld() =     echo "Hello from Nim"  nativeApp("/assets", resizeable = false, title = "hpx_tests")

Здесь мы регистрируем коллбек с именем helloWorld. Никаких аргументов он не принимает. После регистрации коллбеков мы запускаем сервер с помощью функции nativeApp.

Далее в папке /assets находится файл main.nim. Выглядит он следующим образом:

import happyx import assets/native  # working with happyx native  var x = remember 0  proc helloWorld() =   hpxNative.callNim("helloWorld")   x->inc()  appRoutes "app":   "/":     tDiv:       tH1:         "hpx_tests"       tDiv:         "x is {x}"       tButton:         "increase"         @click:           helloWorld()

Со стороны JS мы по нажатию кнопки отправляет событие на сервер, которые принимает и обрабатывает его. В нашем случае мы отправляем вызов helloWorld на сервер. На самом сервере при этом выводится "Hello from Nim".

При этом стоит упомянуть, что на стороне сервера можно напрямую обращаться к приложению и устройству, в котором запущен сервер. Например, при компиляции в Android мы можем отловить сервером событие из JS приложения, при котором мы сможем отобразить нативный Android диалог. Выглядит это следующим образом:

# Main native file import happyx_native  callback:   proc showDialog() =     when defined(export2android):       Log.d("create dialog")       runOnUiThread:         var dialog = AlertDialogBuilder.new(           appContext         ).setTitle(           cast[CharSequence](String.new("title"))         ).setMessage(           cast[CharSequence](String.new("text"))         ).setCancelable(           JVM_TRUE         ).show()         Log.d($dialog.toString())  nativeApp("/assets", resizeable = false, title = "android_tests")

И снова хочется узнать, что вы думаете об этом.

HappyX в проде

С недавних пор одна российская компания стала использовать HappyX в продакшене, через некоторое время выйдет статья об этом.

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

Использовали Nim в своей компании, будь у вас такая возможность?

10.34% Да3
17.24% Было бы интересно поменять/добавить пару скриптов на Nim5
72.41% Нет21

Проголосовали 29 пользователей. Воздержались 2 пользователя.

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


Комментарии

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

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