go-app, ежедневная библиотека для Go

Go

Введение

go-appэто программа, написанная с использованием технологии Go + WebAssemblyПрогрессивные веб-приложениябиблиотека. WebAssembly — это современный код, который может работать в современных браузерах. За последние два года технология WebAssembly сделала большой шаг вперед. Теперь мы можем писать код WebAssembly на языках высокого уровня, таких как C/C++/Rust/Go. я собирался представитьgo-appЭта библиотека упрощает написание кода WebAssembly в Go.

быстрый в использовании

go-appК языковой версии Go предъявляются более высокие требования (Go 1.14+), И должен использоватьGo module. Создайте каталог и инициализируйтеGo Module(Win10 + Гит Баш):

$ mkdir go-app && cd go-app
$ go mod init

Затем скачайте и установитеgo-appСумка:

$ go get -u -v github.com/maxence-charriere/go-app/v6

Что касаетсяGo moduleДля подробного использования см. Fried Fish's.Перейти модули Ultimate вход.

Во-первых, мы собираемся написать программу WebAssembly:

package main

import "github.com/maxence-charriere/go-app/v6/pkg/app"

type Greeting struct {
  app.Compo
  name string
}

func (g *Greeting) Render() app.UI {
  return app.Div().Body(
    app.Main().Body(
      app.H1().Body(
        app.Text("Hello, "),
        app.If(g.name != "",
          app.Text(g.name),
        ).Else(
          app.Text("World"),
        ),
      ),
    ),
    app.Input().
      Value(g.name).
      Placeholder("What is your name?").
      AutoFocus(true).
      OnChange(g.OnInputChange),
  )
}

func (g *Greeting) OnInputChange(src app.Value, e app.Event) {
  g.name = src.Get("value").String()
  g.Update()
}

func main() {
  app.Route("/", &Greeting{})
  app.Run()
}

существуетgo-appиспользуется вкомпонентыЧтобы разделить функциональные модули, каждая структура компонента должна быть встроенаapp.Compo.组件要实现Render()Метод, когда вам нужно отобразить компонент, вызывает этот метод, возвращает отображение страницы.go-appиспользоватьДекларативный синтаксис, вы можете полностью писать HTML-страницы с помощью Go, а описанную выше часть рисования HTML легче понять. Приведенный выше код также реализует функцию поля ввода и добавляет к нему прослушиватель. Всякий раз, когда содержимое поля ввода изменяется,OnInputChangeбудет вызван метод,g.Update()вызовет повторный рендеринг компонента.

Наконец, смонтируйте компонент по пути/начальство.

После написания программы WebAssembly ее необходимо кросс-компилировать в.wasmдокумент:

$ GOARCH=wasm GOOS=js go build -o app.wasm

Если есть ошибка компиляции, используйтеgo versionКоманда, чтобы проверить, является ли Go версией 1.14 или новее.

Далее нам нужно написать веб-программу Go, чтобы использовать это.app.wasm:

package main

import (
  "log"
  "net/http"

  "github.com/maxence-charriere/go-app/v6/pkg/app"
)

func main() {
  h := &app.Handler{
    Title:  "Go-App",
    Author: "dj",
  }

  if err := http.ListenAndServe(":8080", h); err != nil {
    log.Fatal(err)
  }
}

go-appпредоставилapp.Handlerструктура, он автоматически найдет файлы в том же каталогеapp.wasm(Именно поэтому для целевого файла установлено значениеapp.wasmпричина). Затем мы скомпилируем сгенерированныйapp.wasmПоместите его в тот же каталог и выполните программу:

$ go run main.go

Дисплей по умолчанию"Hello World":

После ввода в поле ввода дисплей изменится:

можно увидеть,go-appУстановите для нас некоторые основные стили, веб-иконки и т. д.

простой принцип

Эта диаграмма на GitHub прекрасно иллюстрирует поток выполнения HTTP-запроса:

Запросы пользователей на первом местеapp.Handlerслой и он пойдетapp.wasmВыполните соответствующую логику маршрутизации и перейдите на диск, чтобы найти статические файлы. ответ черезapp.HandlerПеревод возвращается пользователю. пользователи видятapp.wasmРендеринг страницы. Фактически, в этой статье нам нужно только написать веб-программу GO, после каждой записи новой Webassembly, скопируйте новые файлы для компиляции сгенерированного App.wasm Rerun Далее процедуры веб-каталога. Уведомление,Если странице не удалось обновить кеш, это может быть вызвано тем, что вы можете попробовать очистить кеш браузера..

компоненты

Настроить компонент легко, просто добавьтеapp.CompoЕго можно встроить в структуру. выполнитьRender()методы определяют внешний вид компонента, фактическиapp.CompoСуществует внешний вид по умолчанию, который мы можем просмотреть следующим образом:

func main() {
  app.Route("/app", &app.Compo{})
  app.Run()
}

Компилировать и генерироватьapp.wasmПосле этого начальную программу Go Web модифицировать не нужно, достаточно запустить ее напрямую и открыть браузер для просмотра:

обработка событий

существуетбыстрый старт, мы также рассмотрели, как использовать события. Используйте декларативный синтаксисapp.Input().OnChange(handler)Вы можете отслеживать изменения контента. Обработчик события должен бытьfunc (src app.Value, e app.Event)тип,app.Valueявляется триггерным объектом,app.Eventявляется содержанием события. пройти черезapp.ValueМы можем получить такую ​​информацию, как содержимое поля ввода, параметры поля выбора и т. д.app.EventВы можете получить информацию о событии, будь то событие мыши, событие клавиатуры или другие события:

type ShowSelect struct {
  app.Compo
  option string
}

func (s *ShowSelect) Render() app.UI {
  return app.Div().Body(
    app.Main().Body(
      app.H1().Body(
        app.If(s.option == "",
          app.Text("Please select!"),
        ).Else(
          app.Text("You've selected "+s.option),
        ),
      ),
    ),
    app.Select().Body(
      app.Option().Body(
        app.Text("apple"),
      ),
      app.Option().Body(
        app.Text("orange"),
      ),
      app.Option().Body(
        app.Text("banana"),
      ),
    ).
      OnChange(s.OnSelectChange),
  )
}

func (s *ShowSelect) OnSelectChange(src app.Value, e app.Event) {
  s.option = src.Get("value").String()
  s.Update()
}

func main() {
  app.Route("/", &ShowSelect{})
  app.Run()
}

Приведенный выше код отображает поле выбора, и при изменении параметра текст, отображаемый выше, изменится соответствующим образом. Изначально:

После выбора:

вложенные компоненты

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

type Greeting struct {
  app.Compo
}

func (g *Greeting) Render() app.UI {
  return app.P().Body(
    app.Text("Hello, "),
    &Name{name: "dj"},
  )
}

type Name struct {
  app.Compo
  name string
}

func (n *Name) Render() app.UI {
  return app.Text(n.name)
}

func main() {
  app.Route("/", &Greeting{})
  app.Run()
}

Приведенный выше код находится в компонентеGreetingвстроенный вNameкомпонент, работает шоу:

жизненный цикл

go-appПредоставляет функции ловушек для 3 жизненных циклов компонентов:

  • OnMount: вызывается, когда компонент вставлен в DOM;
  • OnNav: вызывается при загрузке и обновлении страницы, на которой находится компонент;
  • OnDismount: вызывается при удалении компонента со страницы.

Например:

type Foo struct {
  app.Compo
}

func (*Foo) Render() app.UI {
  return app.P().Body(
    app.Text("Hello World"),
  )
}

func (*Foo) OnMount() {
  fmt.Println("component mounted")
}

func (*Foo) OnNav(u *url.URL) {
  fmt.Println("component navigated:", u)
}

func (*Foo) OnDismount() {
  fmt.Println("component dismounted")
}

func main() {
  app.Route("/", &Foo{})
  app.Run()
}

Скомпилируйте и запустите, откройте страницу в браузере, откройтеконсоль браузераНаблюдайте за выводом:

component mounted
component navigated: http://localhost:8080/

писать HTML

В предыдущем примере мы видели, как использоватьДекларативный синтаксисПишите HTML-страницы.go-appСоответствующие типы предусмотрены для всех стандартных элементов HTML. Названия методов, создающих эти объекты, тоже легче запомнить, то есть первая буква имени элемента пишется с большой буквы. какapp.Div()Создаватьdivэлемент,app.P()Создаватьpэлемент,app.H1()Создаватьh1элементы и т.д. существуетgo-app, эти структуры предоставляют соответствующие интерфейсы для использования разработчиками, такие какdivсоответствоватьHTMLDivинтерфейс:

type HTMLDiv interface {
  Body(nodes ...Node) HTMLDiv
  Class(v string) HTMLDiv
  ID(v string) HTMLDiv
  Style(k, v string) HTMLDiv

  OnClick(h EventHandler) HTMLDiv
  OnKeyPress(h EventHandler) HTMLDiv
  OnMouseOver(h EventHandler) HTMLDiv
}

Вы можете видеть, что каждый метод возвращает этоHTMLDivсама, так что поддержитецепной вызов. Вызовите эти методы, чтобы установить различные аспекты элемента:

  • Class: добавить класс CSS;
  • ID: установить атрибут id;
  • Style: установить встроенный стиль;
  • Body: Установите содержимое элемента, которое может быть вложено по желанию.divсодержитh1иp,pсодержитimgЖдать;

И установить слушатели событий:

  • OnClick: событие щелчка;
  • OnKeyPress: ключевое событие;
  • OnMouseOver: Наведите указатель мыши на событие.

Например следующий код:

app.Div().Body(
  app.H1().Body(
    app.Text("Title"),
  ),
  app.P().ID("id").
    Class("content").Body(
      app.Text("something interesting"),
    ),
)

Эквивалент HTML-кода:

<div>
  <h1>title</h1>
  <p id="id" class="content">
    something interesting
  </p>
</div>

Родной элемент

мы можемapp.Raw()Пишите HTML-код прямо вapp.Raw()создаст соответствующийapp.UIвозвращение:

svg := app.Raw(`
<svg width="100" height="100">
    <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
`)

Но такой способ написания небезопасен, потому что структура HTML не проверяется.

состояние

В примере мы имели в начале использования условных операторов, условных операторов, соответствующих трем методам:If()/ElseIf()/Else().

IfиElseIfПолучает два параметра, первый параметрboolценность. еслиtrue, второй параметр (типаapp.UI), иначе не отображается.

ElseДолжен бытьIfилиElseIfПосле использования, если ни одно из предыдущих условий не выполнено, отобразить входящийElseметодapp.UI:

type ScoreUI struct {
  app.Compo
  score int
}

func (c *ScoreUI) Render() app.UI {
  return app.Div().Body(
    app.If(c.score >= 90,
      app.H1().
        Style("color", "green").
        Body(
          app.Text("Good!"),
        ),
    ).ElseIf(c.score >= 60,
      app.H1().
        Style("color", "orange").
        Body(
          app.Text("Pass!"),
        ),
    ).Else(
      app.H1().
        Style("color", "red").
        Body(
          app.Text("fail!"),
        ),
    ),
    app.Input().
      Value(c.score).
      Placeholder("Input your score?").
      AutoFocus(true).
      OnChange(c.OnInputChange),
  )
}

func (c *ScoreUI) OnInputChange(src app.Value, e app.Event) {
  score, _ := strconv.ParseUint(src.Get("value").String(), 10, 32)
  c.score = int(score)
  c.Update()
}

func main() {
  app.Route("/", &ScoreUI{})
  app.Run()
}

Выше мы отображаем соответствующий текст в соответствии с введенным счетом,90и выше показаны зеленымGood!,60-90между апельсиномPass!, меньше, чем60показать красныйFail!. Ниже приведен текущий результат:

Range

Предположим, мы хотим написать список HTML, в настоящее время есть ломтик строки. Если писать слишком громоздкое и негибкое, и склонна к ошибке. Затем вы можете использоватьRange()Метод:

type RangeUI struct {
  app.Compo
  name string
}

func (*RangeUI) Render() app.UI {
  langs := []string{"Go", "JavaScript", "Python", "C"}
  return app.Ul().Body(
    app.Range(langs).Slice(func(i int) app.UI {
      return app.Li().Body(
        app.Text(langs[i]),
      )
    }),
  )
}

func main() {
  app.Route("/", &RangeUI{})
  app.Run()
}

Range()можно нарезать илиmapГенерируетapp.UI, а затем расположите плитку над элементомBody()метод.

результат операции:

контекстное меню

существуетgo-app, мы можем легко настроить всплывающее меню, вызываемое правой кнопкой мыши, и написать ответы для пунктов меню:

type ContextMenuUI struct {
  app.Compo
  name string
}

func (c *ContextMenuUI) Render() app.UI {
  return app.Div().Body(
    app.Text("Hello, World"),
  ).OnContextMenu(c.OnContextMenu)
}

func (*ContextMenuUI) OnContextMenu(src app.Value, event app.Event) {
  event.PreventDefault()

  app.NewContextMenu(
    app.MenuItem().
      Label("item 1").
      OnClick(func(src app.Value, e app.Event) {
        fmt.Println("item 1 clicked")
      }),
    app.MenuItem().Separator(),
    app.MenuItem().
      Label("item 2").
      OnClick(func(src app.Value, e app.Event) {
        fmt.Println("item 2 clicked")
      }),
  )
}

func main() {
  app.Route("/", &ContextMenuUI{})
  app.Run()
}

мы вOnContextMenuпозвонил вevent.PreventDefault()Предотвращает всплывающее меню по умолчанию. Смотрите бегущий результат:

Нажмите на пункт меню и наблюдайте за выводом консоли~

app.Handler

Выше мы используемgo-appВстроенныйapp.HandlerОбработка клиентских запросов. Мы просто устанавливаем простые две свойстваAuthorиTitle.app.HandlerЕсть много других полей, которые можно настроить:

type Handler struct {
  Author string
  BackgroundColor string
  CacheableResources []string
  Description string
  Env Environment
  Icon Icon
  Keywords []string
  LoadingLabel string
  Name string
  RawHeaders []string
  RootDir string
  Scripts []string
  ShortName string
  Styles []string
  ThemeColor string
  Title string

  UseMinimalDefaultStyles bool
  Version string
}
  • Icon: установить значок приложения;
  • Styles: файл стилей CSS;
  • Scripts: Файл сценария JS.

Файлы CSS и JS должны быть вapp.Handlerзаявление в. Ниже приведен примерapp.Handler:

h := &app.Handler{
  Name:        "Luck",
  Author:      "Maxence Charriere",
  Description: "Lottery numbers generator.",
  Icon: app.Icon{
    Default: "/web/icon.png",
  },
  Keywords: []string{
    "EuroMillions",
    "MEGA Millions",
    "Powerball",
  },
  ThemeColor:      "#000000",
  BackgroundColor: "#000000",
  Styles: []string{
    "/web/luck.css",
  },
  Version: "wIKiverSiON",
}

Код этой статьи

Код WebAssembly для этой статьи находится в отдельном каталоге. Демонстрационный код Go Web находится в веб-каталоге. Сначала введите каталог и скомпилируйте с помощью следующей команды:

$ GOARCH=wasm GOOS=js go build -o app.wasm

затем будет генерироватьapp.wasmскопировать вwebсодержание:

$ cp app.wasm ../web/

Перейдите в веб-каталог и запустите сервер:

$ cd ../web/
$ go run main.go

Суммировать

В этой статье описывается, как использоватьgo-appНаписание веб-приложений на основе WebAssembly. Некоторые люди могут подумать,go-appТо, как вы пишете HTML, немного громоздко. Но мы можем написать программу преобразования для преобразования обычного HTML-кода вgo-appКод, если интересно, можете реализовать сами. Технология WebAssembly очень достойна внимания~

Если вы найдете забавную и простую в использовании языковую библиотеку Go, вы можете отправить сообщение о проблеме в ежедневной библиотеке Go GitHub😄

Ссылаться на

  1. go-приложение GitHub:GitHub.com/Ma Shaun Test-Check…
  2. Перейти на ежедневный репозиторий GitHub:GitHub.com/Darenjun/go-of…

я

мой блог:darjun.github.io

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~