この記事はあくあたん工房AdventCalendar24日目の記事です.
はじめに
Electronより軽量なHTML5デスクトップアプリ用ライブラリとして最近注目されているlorcaを使ってデスクトップアプリを作っていきます.
やることは簡単で,
- Reactで適当なSPAを作る
- Ginで静的ファイルを配信するエンドポイントを立てる
- lorcaでそれを見る
という感じです.
lorca
READMEより
A very small library to build modern HTML5 desktop apps in Go. It uses Chrome browser as a UI layer. Unlike Electron it doesn't bundle Chrome into the app package, but rather reuses the one that is already installed. Lorca establishes a connection to the browser window and allows calling Go code from the UI and manipulating UI from Go in a seamless manner.
上記のとおり,Electronと決定的に違うのは,ユーザーがすでにインストールしているChromeをUIレイヤーとして活用するという点です.
これによって生成されるアプリケーションのサイズが軽量になります.
(もちろん,Chromeのタブの1つとして動作しているのでElectronより機能はかなり制限されています. e.g window menuが無い)
ただ,GoのコードとJSのコードを紐付けることができるので,GoのCLIアプリをGUI化するときにめっちゃ役立ちそうです.今回は使いませんが.
作っていく
最初のリポジトリ構造はこれ
.
├── LICENSE
├── README.md
├── go.mod
├── go.sum
└── main.go
この中にcreate-react-app
でReactアプリを作ります.
$ create-react-app app
$ tree . -I node_modules
.
├── LICENSE
├── README.md
├── app
│ ├── README.md
│ ├── build
│ │ ├── asset-manifest.json
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── precache-manifest.1b7c797c02d13deffb0ad84befafc406.js
│ │ ├── service-worker.js
│ │ └── static
│ │ ├── css
│ │ │ ├── main.5be34591.chunk.css
│ │ │ └── main.5be34591.chunk.css.map
│ │ ├── js
│ │ │ ├── 1.fa92c112.chunk.js
│ │ │ ├── 1.fa92c112.chunk.js.map
│ │ │ ├── main.88557385.chunk.js
│ │ │ ├── main.88557385.chunk.js.map
│ │ │ ├── runtime~main.229c360f.js
│ │ │ └── runtime~main.229c360f.js.map
│ │ └── media
│ │ └── logo.5d5d9eef.svg
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ └── manifest.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.js
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ └── serviceWorker.js
│ └── yarn.lock
├── go.mod
├── go.sum
└── main.go
yarn build
するとbuild
ディレクトリにwebpack後のファイルが展開されるので,これを配信するサーバーとそれをlorcaで参照する処理を書いていきます.
package main
import (
"log"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/zserge/lorca"
)
var staticDir = "./app/build"
func main() {
go runServer()
err := runUI()
if err != nil {
log.Fatal(err)
}
}
// app/build/以下を配信するサーバー
func runServer() {
router := gin.Default()
router.Use(static.Serve("/", static.LocalFile(staticDir, true)))
err := router.Run(":9000")
if err != nil {
log.Fatal(err)
}
}
// lorca起動
func runUI() error {
ui, err := lorca.New("", "", 320, 480)
if err != nil {
return err
}
ui.Load("http://localhost:9000")
<-ui.Done()
return nil
}
実行するとcreate-react-app
直後のお馴染みの画面がデスクトップアプリのような形で表示されると思います.
$ go run main.go
おぉ〜.
1つのデスクトップアプリケーションとしてパッケージ化する
公式のexampleにあるcounterを見てもらうと分かりますが,Windows,MacOS,Linuxごとにアプリをパッケージ化できるようなシェルスクリプトが書いてあります.
パクり参考にしながら今回の構成に合わせるとこんな感じになります.
#!/bin/sh
APP="Example.app"
mkdir -p $APP/Contents/{MacOS,Resources}
go build -ldflags "-X main.staticDir=$(pwd)/Example.app/Contents/Resources/app" -o $APP/Contents/MacOS/lorca-example
cat > $APP/Contents/Info.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>lorca-example</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundleIdentifier</key>
<string>com.zserge.lorca.example</string>
</dict>
</plist>
EOF
cp icons/icon.icns $APP/Contents/Resources/icon.icns
cp -r app/build/ $APP/Contents/Resources/app
find $APP
-ldfrags
で埋め込んでるリソースのパス指定の方法が良くないですね.はい.
というかシングルバイナリ化したほうがいいのでしょうか?
スクリプトを走らせると,Example.app
ができているはずです.
おわりに
今回のリポジトリはこちら : taxio/temo
本当はMacOS用の簡単なメモ帳アプリを作りたかったんですが,クリスマスケーキとチーズフォンデュが僕を呼んでいるので今日はここまで.
リポジトリのコードはちょくちょく更新していきます.