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