Изначально библиотека RUI разработана для создания клиент-серверных веб приложений на языке go, где вся обработка осуществляется на сервере, а браузер используется только как тонкий клиент.
Однако в последней версии библиотеки (0.10.0) была добавлена поддержка технологии WebAssembly. Теперь стало возможным объединить серверную и клиентскую часть в единый модуль исполняемый в браузере. При этом требуются минимальные изменения в уже существующем проекте использующем библиотеку RUI
В этой статье я покажу как переделать уже существующий проект чтобы его можно было скомпилировать в wasm модуль и запустить его в браузере
Рассматривать данный процесс будем на примере демонстрационного приложения библиотеки RUI.
Для того чтобы подготовить проект в него необходимо внести следующие изменения:
добавить страницу загрузки приложения;
вынести из ресурсов приложения все мультимедиа файлы (графику, видео, аудио);
где необходимо, добавить в 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"
На этом все