Фронтенд на Go с использованием библиотеки RUI

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Изначально библиотека RUI (https://github.com/anoshenko/rui) разработана для создания клиент-серверных веб приложений на языке go, где вся обработка осуществляется на сервере, а браузер используется только как тонкий клиент.

Однако в последней версии библиотеки (0.10.0) была добавлена поддержка технологии WebAssembly. Теперь стало возможным объединить серверную и клиентскую часть в единый модуль исполняемый в браузере. При этом требуются минимальные изменения в уже существующем проекте использующем библиотеку RUI

В этой статье я покажу как переделать уже существующий проект чтобы его можно было скомпилировать в wasm модуль и запустить его в браузере

Рассматривать данный процесс будем на примере демонстрационного приложения библиотеки RUI: https://github.com/anoshenko/ruiDemo.

Для того чтобы подготовить проект в него необходимо внести следующие изменения изменения:

  1. добавить страницу загрузки приложения

  2. вынести из ресурсов приложения все мультимедиа файлы (графику, видео, аудио)

  3. где необходимо, добавить в ImageView свойство "srcset"

Страница загрузки приложения

Страница загрузки приложения уже добавлена в проект (файл "wasm_exec.html") и выглядит она следующим образом

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>Go wasm</title>
</head>

<body>
   <script src="wasm_exec.js"></script>
   <script>
        if (WebAssembly) {
            const go = new Go();
            WebAssembly.instantiateStreaming(fetch("demo.wasm"), go.importObject).then((result) => {
                go.run(result.instance);
            });
        } else {
            console.log("WebAssembly is not supported in your browser")
        }
   </script>
</body>

</html>

Это стандартная методика загрузки wasm модуля. Такой код вы можете увидеть практически в любом проекте на go для WebAssembly. Просто копируйте эту страницу в ваш проект

Код страницы очень простой: он загружает файл "demo.wasm" и запускает его.

Однако возникает вопрос: что за файл "wasm_exec.js" и где его взять?

Файл "wasm_exec.js" это файл из стандартной библиотеки golang. Он используется для согласования типов данных go и WebAssembly. Найти его можно по адресу "$GOROOT/misc/wasm/wasm_exec.js".

ВНИМАНИЕ. Файл "wasm_exec.js" может изменяться. Поэтому если вы переходите на новую версию go, то необходимо обновить и "wasm_exec.js" на вашем веб сервере. Если этого не сделать то wasm модуль может перестать загружаться 

Вынесение из ресурсов приложения всех мультимедиа файлов

Приложение скомпилированное в wasm модуль, по умолчанию, не умеет загружать картинки, видео и аудио из ресурсов приложения. Поэтому их необходимо исключить из модуля и разместить на сервере.

В принципе, данные файлы можно было бы оставить в ресурсах и подключать, например, так

imageView.Set(rui.Source, "data:image/png;base64,"+base64.StdEncoding.EncodeToString(data))

Однако этот способ вызывает жуткие тормоза в Safari и не рекомендуется к использованию (при этом в хроме и мозилле все работает быстро, никаких задержек нет).

Кроме того включение мультимедиа в ресурсы значительно увеличивает размер и так не маленького модуля, что, в свою очередь, увеличивает время загрузки страницы (а это всегда плохо).

Для того чтобы исключить все мультимедиа файлы, в проекте они вынесены в отдельную папку "media" и их включение в ресурсы осуществляется только если целевая платформа не js/wasm. Для всех платформ за исключение js/wasm ресурсы объявляются в файле resources.go

//go:build !wasm

package main

import "embed"

//go:embed resources media
var resources embed.FS

А для платформы js/wasm ресурсы объявляются в файле resourcesWasm.go

//go:build wasm

package main

import "embed"

//go:embed resources
var resources embed.FS

Добавление в ImageView свойства "srcset"

Библиотека RUI поддерживает возможность использования разных вариантов изображения для различных плотностей пикселей экрана. Для этого в ресурсы приложения включатся несколько версий изображения. Например в рассматриваемом проекте это cat.jpg и cat@2x.jpg, sample.png, sample@2x.png и sample@3x.png. Приложение само анализирует какие версии картинки есть и использует нужную. 

Однако в случае wasm модуля картинки расположены на внешнем сервере и приложение не знает какие версии изображения имеются. Поэтому для платформы wasm необходимо с помощью свойства "srcset" явно задавать список вариантов изображений. Например:

if runtime.GOOS == "js" {
	imageView.Set(rui.SrcSet, "cat.jpg, cat@2x.jpg")
}

В свойстве "srcset" вы должны перечислить через запятую все варианты изображения.

Это все изменения которые необходимо сделать в проекте для поддержки технологии WebAssembly

Компиляция и запуск

Теперь необходимо скомпилировать проект. Для этого переходим в папку проекта и выполняем команду

GOOS=js GOARCH=wasm go build -o demo.wasm

В результате у вас появится файл demo.wasm.

Теперь если вы попытаетесь открыть в браузере wasm_exec.html, то вы увидите пустую страницу, а в консоли браузера следующую ошибку

Access to fetch at 'file://.../demo.wasm' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.

Этот говорит нам о том, что нам нужен http сервер. Напишем его на go

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

var projectPath = "<your path>/ruiDemo"

func main() {
    http.HandleFunc("/", server)
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

func server(w http.ResponseWriter, req *http.Request) {
    switch req.Method {
    case "GET":
        fmt.Print(req.URL.Path)

        switch req.URL.Path {
        case "/", "/wasm_exec.html":
            http.ServeFile(w, req, projectPath+"/wasm_exec.html")

        case "/demo.wasm", "/wasm_exec.js":
            http.ServeFile(w, req, projectPath+req.URL.Path)

        default:
            path := projectPath + "/media/images" + req.URL.Path
            if _, err := os.Stat(path); err == nil {
                http.ServeFile(w, req, path)
                return
            }
            path = projectPath + "/media/raw" + req.URL.Path
            http.ServeFile(w, req, path)
        }
    }
}

После запуска данного сервера и перехода в браузере по адресу "localhost:8080" вы должны увидеть демо приложение библиотеки RUI

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

Uncaught (in promise) TypeError: Failed to execute 'compile' on 'WebAssembly': Incorrect response MIME type. Expected 'application/wasm'

Для решения этой проблемы необходимо прописать в настройках сервера для файлов с расширением "wasm" mime-тип "application/wasm"

На этом все

Источник: https://habr.com/ru/post/698818/


Интересные статьи

Интересные статьи

Я изучаю Битрикс где-то пару недель. Зачем?.. Хотелось чего-то новенького, тут подвернулась учёба. Даром, с наставниками, с возможным трудоустройством дальше хотя бы на пару месяцев - на испытательный...
Перевод главы 13 Параллелизм из книги ‘Expert Python Programming’, Second Edition Michał Jaworski & Tarek Ziadé, 2016 Асинхронное программирование В последние годы асинхронное прог...
В последние годы все чаще говорят о Trello, как о прекрасном инструменте для организации и планирования. В нашей компании мы вот уже 3 года используем Trello для планирования многих процессов, на...
Функциональное программирование в Scala может быть нелегко освоить из-за некоторых синтаксических и семантических особенностей языка. В частности, некоторые средства языка и способы реализации за...
Эта статья посвящена одному из способов сделать в 1с-Битрикс форму в всплывающем окне. Достоинства метода: - можно использовать любые формы 1с-Битрикс, которые выводятся компонентом. Например, добавле...