Ой, все чудесится и чудесится!
— Л. Кэррол, Алиса в Стране Чудес

-
Вас задрало, что node_modules на простом сайте соревнуются по количеству используемого места с вашей коллекцией музыки?
-
Вы перечитали инструкцию к Redux в шестидесятый раз и поняли две вещи: «До меня кажется доходит…» и «Думаю, мне стоит перечитать это ещё раз!»
-
Вы в очередной раз узнали, что 1 + «1» == «11», а [] — {} == NaN?
-
Билд скрипт в webpack занимает больше места чем ваша библиотека на javascript?
Тогда заходите под кат, я покажу вам, как можно перевести ваш фронтэнд на го.
Встречаем, vugu. Молодая (сразу предупреждаю, не релизнутая) и очень интересная библиотека, которая позволяет вам использовать golang напрямую в html. Естественно, так как пока не существует браузеров со встроенной поддержкой golang, то реализовывать всё пришлось через WASM.
Vugu это очень молодая библиотека и упоминаний о ней на хабре я не нашёл, за исключением пары дайджестов.
Давайте посмотрим и потрогаем эту библиотеку изнутри. И так, что же такое vugu?
Для начала, давайте представим, что вы пишите html компонент, только скрипты имеют тип application/x-go вместо javascript:
<div> <p vg-if='c.ShowText'> Conditional text here. </p> </div> <script type="application/x-go"> type Root struct { // component for "root" ShowText bool `vugu:"data"` } </script>
Вы сохраняете вышеописанное безобразие в файл с расширением *.vugu и запускаете стороннюю библиотеку, которая жрёт 5 гигов памяти. Ладно, шучу, на самом деле vugu был создан с целью упростить процесс разработки, вместо того, чтобы его усложнить. Всё что вам необходимо делать в vugu можно сделать средствами самого go. Для компиляции приложения можно воспользоваться простыми:
go generate go build ./file-name
В комплекте идёт утилита, для облегчения разработки, под названием vgrun, но это просто обёртка над стандартными командами. Для простоты иллюстраций я буду использовать именно эту библиотеку.
vgrun devserver.go
запустит простой сервер, и начнёт следить за файлами на предмет изменений. Если оные находятся, то программа автоматически перезапускает сервер и обновляет приложение. Просто и без наворотов.
Что же, пришло время проследить путь vugu файла до конечного пользователя.
-
Файл парсится html парсером. Да, файл должен содержать полностью рабочий HTML.
-
После этого файл ещё раз парсится vugu парсером. Тут файл разбирается на части и пересобирается в go. Сгенерированный код это полностью рабочий код на golang.
-
Полученный файл компилируется в WASM и упаковывается в web assembly для запуска на клиенте.
-
PROFIT!
Ну вот и всё. Можете расходиться. Тут всё понятно.
Что? Надо больше? Ладно, так уж и быть. Давайте закапываться глубже и делать больше. Давайте для начала посмотрим на сгенерированный golang файл. Файл называется 0_components_vgen.go. В него и пойдём.
Код из <script type="application/x-go"> из vugu переносится в golang без каких либо вопросов и изменений. Приятно. В дополнение к этому, в файле создаётся функция Build, которая генерирует HTML интерфейс.
func (c *Root) Build(vgin *vugu.BuildIn) (vgout *vugu.BuildOut) { vgout = &vugu.BuildOut{} var vgiterkey interface{} _ = vgiterkey var vgn *vugu.VGNode vgn = &vugu.VGNode{Type: vugu.VGNodeType(3), Namespace: "", Data: "div", Attr: []vugu.VGAttribute{vugu.VGAttribute{Namespace: "", Key: "class", Val: "demo"}}} vgout.Out = append(vgout.Out, vgn) // root for output { vgparent := vgn _ = vgparent vgn = &vugu.VGNode{Type: vugu.VGNodeType(1), Data: "\n "} vgparent.AppendChild(vgn) vgn = &vugu.VGNode{Type: vugu.VGNodeType(3), Namespace: "", Data: "button", Attr: []vugu.VGAttribute(nil)} vgparent.AppendChild(vgn) vgn.DOMEventHandlerSpecList = append(vgn.DOMEventHandlerSpecList, vugu.DOMEventHandlerSpec{ EventType: "click", Func: func(event vugu.DOMEvent) { c.HandleCat(event) }, // TODO: implement capture, etc. mostly need to decide syntax })
Выглядит запутанно, как и любой сгенерированный код, но если присмотреться, то можно запросто увидеть что это просто наш HTML, созданный программно.
После всего, vugu собирает все компоненты вашего сайта в единый программный блок (а можно и не в единый) и отправляет всё это на клиент. Библиотека в состоянии отслеживать DOM и DOMEvents для того, чтобы передавать события от HTML компонентов в ваш код на golang.
Ну вот. Всё достаточно просто. На самом деле, всё очень просто. vugu построена на этом подходе. Vugu — это не фреймворк. Это библиотека, которую вы можете использовать в некоторых частях вашего проекта. Вы можете программно вызывать рендер определённых компонентов там, где вам это нужно. Вам не придётся зависеть от create-react-app или чего-то ещё. Всё очень легковесно.
Ну что же, хватит болтовни, давайте напишем простенькое приложение, чтобы показать, что можно и чего нельзя делать с помощью vugu.
Для начала сделаем:
go get -u github.com/vugu/vgrun vgrun -install-tools
После этого можно создать проект из темплейта:
vgrun -new-from-example=simple .
Ну и запустить всё это дело на локальном девсервере:
vgrun devserver.go
Если в этот момент у вас вылетит пара ошибок о том, что у вас не достаёт каких-либо модулей, следуйте инструкциям и запустите go get. Последняя версия golang не признаёт зависимостей в go.mod и вам придётся загрузить их руками.
vscode имеет функцию подсветки синтаксиса для vugu. Приятный плюс. Устанавливаем эту подсветку и начинаем писать наш root.vugu.
Для простоты душевной напишем программу, которая будет показывать фотографию котиков. Интернет ведь создавался для котиков, так ведь?
В начале каждого vugu файла находится HTML разметка компонента.
<div class="demo"> <button @click="c.HandleCat(event)">Get a cat!</button> <div vg-if='c.IsLoading'>Loading...</div> <div vg-if='len(c.Cats) > 0'> <div vg-for='c.Cats'> <img :src='value.URL' alt="cat"></img> </div> </div> </div>
Тут всё достаточно просто. И в принципе, понятно для любого человека, который работал с vue.js. Для тех, кто не работал с vue, разобраться не составит большого труда.
События определяются с помощью @. @click, например, это ваш обработчик события, который запустится по нажатию на кнопку. Самое приятное, сюда можно запихнуть функцию или напрямую писать golang код.
Вы можете показывать определённый контент используя аттрибут vg-if.
<div vg-if='c.IsLoading'>Loading...</div>
Соответственно Loading… будет показан только когда переменная IsLoading равняется true (Откуда взялся этот «с» я объясню попозжее). Сюда тоже можно запихивать любой golang код, как видно на следующей строке.
После этого мы будем использовать vg-for для того, чтобы сгенерировать вывод для каждого элемента в коллекции.
Ну и на закуску, если вы добавляете двоеточие в начале HTML атрибута, то значение этого атрибута будет взято из golang кода.
Дальше у нас начинаются чудеса и самая интересная часть программы.
<script type="application/x-go"> import ( "encoding/json" "net/http" "log" ) type Root struct { IsLoading bool `vugu:"data"` Cats []Cat `vugu:"data"` } type Cat struct { ID string `json:"id"` URL string `json:"url"` Width int `json:"width"` Height int `json:"height"` }
Здесь я определяю две структуры, Cat, это для поддержки котиков в API и Root, основной структуры в программе. Название этой структуры должно совпадать с названием файла, и она должна быть экспортирована (начинаться с заглавной буквы). Поля этой структуры должны быть отмечены тэгом `vugu:"data"`. Всё отмеченное этим тегом будет доступно в нашем HTML коде через переменную с.
Root это название нашего компонента. Root это специальный компонент в vugu. Маунт-поинт вашего приложения начинается с Root.
Количество vugu файлов не ограничено. Создавайте столько компонентов, сколько душе угодно. Если вам приспичило назвать что-то двумя словами, то файл должен называться: koshki-sobachki.vugu а основной тип в этом файле KoshkiSobachki.
Раутинг будет создан автоматически, основываясь на называниях компонентов. Соответственно вышеописанный компонент будет создан и смонтирован по адресу /KoshkiSobachki. Хотя, опять же, vugu не очень любит всю эту магию и синтаксический сахар. Весь раутинг можно переопределить вручную.
Ладно, давайте закончим писать наш простой сайт.
func (c *Root) HandleCat(event vugu.DOMEvent) { ee := event.EventEnv() go func() { ee.Lock() c.IsLoading = true ee.UnlockRender() client := &http.Client{} req, _ := http.NewRequest("GET", "https://api.thecatapi.com/v1/images/search?limit=3&size=full", nil) req.Header.Set("x-api-key", "710c211b") res, err := client.Do(req) if err != nil { log.Printf("Error fetching: %v", err) return } defer res.Body.Close() var newcat []Cat err = json.NewDecoder(res.Body).Decode(&newcat) if err != nil { log.Printf("Can't unmarshal the json: %v", err) return } ee.Lock() defer ee.UnlockRender() c.Cats = newcat c.IsLoading = false }() }
Код достаточно прост. Идём на https://thecatapi.com, регистрируемся, получаем бесплатный API-Key и грузим его в код. (Не бойтесь, ключ в этом примере не валидный, так что вы не сможете угнать у меня мой любимый сервис генерации котиков). Две вещи, которые надо здесь упомянуть это:
-
Код обработчика события напрямую запускает goroutine и не блокирует сам обработчик событий.
-
В коде goroutine мы будем использовать EventEnvironment для того, чтобы синхронизировать доступ к данным. Перед тем, как вы обновляете поля структуры с вам необходимо вызвать Lock() а сразу после UnlockRender().
По нажатию на кнопку, мы получаем json с тремя объектами, содержащими ссылки на котиков. Парсим этот json в массив типа Cat, который мы создали заранее. Сохраняем эти данные обратно в переменную Cats в структуре с. Попутно меняем значение переменной IsLoading для того, чтобы отобразить и скрыть div который оповещает о загрузке.
Проверяем:

Замечательно, всё работает.
Давайте погрузимся в некоторые детали vugu.
Главная деталь — ничего не происходит автоматически и без вашего участия. Такова позиция главного разработчика vugu. Всё что вы видите на экране происходит по вашему велению.
Посему, например, CSS, объявленный в vugu файлах будет просто вставлен в ваш HTML. Никаких примочек. Ничего не будет переименовано, и если вы напишите .header в двух разных модулях, у вас произойдёт коллизия стилей. Так что аккуратно.
Компонент, написанный единожды можно использовать внутри других компонентов (так же как и в React и Blazor). Компоненты могут находиться в четырёх состояниях:
-
Init(ctx vugu.InitCtx) компонент создан, но ещё не успел повидать жизнь и выпить пивка.
-
Compute(ctx vugu.ComputeCtx) компонент скоро увидит свет. Пересчитываем переменные, обновляем значения.
-
Rendered(ctx vugu.RenderedCtx) по компоненту как следует прошлись и отрендерили по самые помидоры.
-
Destroy(ctx vugu.DestroyCtx) компонент никому не сдался, и ему пора на свалку. Выгорание, что ещё сказать.
Всё достаточно просто. Все компоненты это просто набор структур с тэгами `vugu:»data»`. Таких структур может быть много.
Приведу ещё примеров с сайта создателя vugu:
<!-- root.vugu --> <div class="root"> <ul> <main:MyLine FileName="example.txt" :LineNumber="rand.Int63n(100)" ></main:MyLine> </ul> </div> <script type="application/x-go"> import "math/rand" </script>
<!-- my-line.vugu --> <li class="my-line"> <strong vg-content='c.FileName'></strong>:<span vg-content='c.LineNumber'></span> </li> <script type="application/x-go"> type MyLine struct { FileName string vugu:"data" LineNumber int vugu:"data" } </script>
В этом примере вы можете видеть, как просто создавать и использовать компоненты в vugu.
Ну и напоследок, wasm файл, который получается на выходе, весит 7 метров. Это на порядок лучше, чем то, что выдаёт Blazor, но мы можем пойти глубже.
Что если я скажу, что вы можете запустить ваш проект написанный на vugu в tinygo? Именно это я вам и говорю.
Идём на https://www.vugu.org/doc/tinygo и запускаем билд либо через докер, либо с помощью синей изоленты и кузькиной матери. На выходе мы получаем замечательный wasm файл, который весит 500килобайт. Ура! Всем по кошаку! Получайте, сколько хотите!
Ладно, хватит разглагольствовать, идём и читаем официальную документацию на https://www.vugu.org/doc.
После этого можно идти и читать намного более объёмную документацию на https://pkg.go.dev/github.com/vugu/vugu/.
Я связался с автором проекта и проверил, проект не запущен, хотя документация устарела. Я лично предложил свою помощь в обновлении документации и развитии проекта, так что vugu в массы. Но, несмотря на это, вот вам официальное заявление:
Проект, всё же, находится в режиме тестирования. Например, последняя версия tinigo сломала компиляцию wasm кода. Так что пользуйтесь на свой страх и риск. Но, пользуйтесь. Это весело.
Если у кого-то есть вопросы — создавайте issue, делайте PR или пишите мне в личку, я могу написать создателям в Slack.
Удачного всем погружения в vugu!
ссылка на оригинал статьи https://habr.com/ru/post/567440/
Добавить комментарий