Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
GopherJS+Vecty
Search
irieda
February 24, 2018
Technology
1.1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
GopherJS+Vecty
Goだけでつくるフロントエンド入門
irieda
February 24, 2018
More Decks by irieda
See All by irieda
SpaGoでSPAを作ろう!
nobonobo
0
660
TinyGoでIoTを始めよう
nobonobo
2
920
Goだけでロビーサーバーを作ってみた
nobonobo
2
580
Other Decks in Technology
See All in Technology
PostgreSQL 19 新機能概要 OSC Hokkaido 2026
nori_shinoda
0
140
[チョークトーク資料]AWS DevOps Agent を使いこなす / AWS Dev Ops Agent Chalk Talk AWS Summit Japan 2026
kinunori
3
570
自宅LLMの話
jacopen
1
650
AIのReact習熟度を測る
uhyo
2
650
[AWS Summit Japan 2026]迷っているあなたへ_小さな一歩が、やがて自分を助けてくれる
sh_fk2
1
160
ぼっちではじめた登壇が「51名」「241件」の発信に化けた
subroh0508
1
240
データレイクの「見えない問題」を可視化する
sansantech
PRO
1
100
iOS アプリの「これって不具合ですか?」を AI に調べてもらう
miichan
0
100
200個のGitHubリポジトリを横断調査したかった
icck
0
140
自分が詳しくない領域でAIを使う #プロヒス2026
konifar
13
5.2k
MUSUBI 田中裕一『AIと共に行う「しごとのリデザイン」- スモールバックオフィス編』AI Ops Lab #4
musubi
0
270
マルチアカウント環境での コーディングエージェントを使った障害調査が大変なので AIエージェントにReadOnly権限を付与してみた / ReadOnly AI Agents for Multi-Account AWS Incident Response
yamaguchitk333
2
110
Featured
See All Featured
A Modern Web Designer's Workflow
chriscoyier
698
190k
Accessibility Awareness
sabderemane
1
140
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
140
Optimising Largest Contentful Paint
csswizardry
37
3.7k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
New Earth Scene 8
popppiees
3
2.3k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
1.7k
Are puppies a ranking factor?
jonoalderson
1
3.6k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
Transcript
Go だけで作る Go だけで作る フロントエンド入門 フロントエンド入門
お前誰よ? お前誰よ? メカトロソフト屋 Pythonista -> Gopher なんでも Go で書いちゃうひと Go
歴は 5 年目 サイト: 会社: 144Lab(2017/07/01 から新社名) HN: @nobonobo http://golang.rdy.jp/
リッチなフロントエンド作りたい? リッチなフロントエンド作りたい? もちろん もちろん 作りたいですよね! 作りたいですよね!
でも・・・ でも・・・
JS を書きたく無いでござる JS を書きたく無いでござる
JS を書きたく無いでござる JS を書きたく無いでござる
絶対に JS を書きたく 絶対に JS を書きたく 無いでござる 無いでござる ( 個人的な感想です)
( 個人的な感想です) そこで そこで
Go から Javascript Go から Javascript もちろん、Go から Javascript への変換には
ですね! 参考情報 Go を JS に 100% 変換できます Go を JS に 100% 変換できます GopherJS http://golang.rdy.jp/2015/10/15/gopherjs/ http://golang.rdy.jp/playgopherjs/#/1
関連ライブラリ 関連ライブラリ トランスパイラ: 仮想 DOM: ルーター: Bootstrap4コンポーネントセット: GopherJS(BSD) Vecty(BSD) github.com/go-humble/router(MIT)
github.com/nobonobo/bootstrap4(MIT)
GopherJS の近況 GopherJS の近況 速度より安定性を重視 出力ターゲットは ES5 ES2015~ES2017 の新記述の最適化がない Go1.10
サポートが着々と進んでる (Go 本体では WebAssembly 実装の実験も!!)
Vecty の近況 Vecty の近況 鋭意安定板に向けて改善真っ只中 昨年中に 5~6 回の破壊的変更がありました 破壊的変更の予定がまだ少しあります 本格利用はしばしお待ちを
HTML/CSS/JS の全てを Go で書ける というわりに CSS 支援は弱い
Vecty の提供機能 Vecty の提供機能 HTML ビルダ レンダラー コンポーネント プロパティ ほんとこれだけ。
HTML ビルダ HTML ビルダ タグ定義を使って生成 属性は Markup でラップして記述 慣れるまで大変 elem.Body(
// <body> elem.Heading1(vecty.Text("TITLE")), // <h1>TITLE</h1> elem.Anchor( // <a href="/page"> vecty.Markup(prop.Href("/page")), vecty.Text("link"), // link ), // </a> ) // </body>
レンダラー レンダラー コンポーネントのレンダリングは二種類ある RenderBody: 初期レンダリング Rerender: 差分レンダリング
コンポーネント コンポーネント Core 実装と Render メソッドを持つ プロパティを元に HTML を生成する実装 コンポーネントツリーが仮想
DOM 相当 type MyComponent struct { vecty.Core } func (c *MyComponent) Render() vecty.ComponentOrHTML { return elem.Header1(vecty.Text("Hello!")) }
プロパティ プロパティ コンポーネント定義のフィールド 任意の型に`vecty: prop `タグをつける コンポーネントが保有・参照するデータ 親から受け取って参照しつつ HTML を組む
type MyComponent struct { vecty.Core Title string `vecty:"prop"` } func (c *MyComponent) Render() vecty.ComponentOrHTML { return elem.Header1(vecty.Text(c.Title)) }
ミニマムな例 ミニマムな例
ミニマムな実装例 ミニマムな実装例 type TopView struct {vecty.Core} func (c *TopView) Render()
vecty.ComponentOrHTML { vecty.SetTitle("TopView") return elem.Body( elem.Heading1(vecty.Text("First")), elem.Button(vecty.Text("click")), ) } func main() { top := &TopView{} vecty.RenderBody(top) }
ミニマムな実行例 ミニマムな実行例 Top click
プロパティな例 プロパティな例
プロパティな実装例 プロパティな実装例 type TopView struct { vecty.Core Heading string `vecty:"prop"`
} func (c *TopView) Render() vecty.ComponentOrHTML { vecty.SetTitle("TopView") return elem.Body( elem.Heading1(vecty.Text(c.Heading)), elem.Button( vecty.Markup( event.Click(func(ev *vecty.Event) { now := time.Now().Format(time.RFC3339Nano) c.Heading = fmt.Sprintf("Top: %s", now) vecty.Rerender(c) })
プロパティな実行例 プロパティな実行例 Top click
Vecty の不足機能 Vecty の不足機能 ルーター/ビュー スタイルシート Flux 相当
ルーター/ ビュー ルーター/ ビュー いくつか試したところ これが Vecty 用でもないけど 使いやすかった。 github.com/go-humble/router(MIT)
スタイルシート スタイルシート 個別のスタイル操作はできる スタイルシートに対する支援がまだない CSS は頑張って用意するしかなさそう
Flux 相当 Flux 相当 サンプルが採用している方法を参考に。 アクション: メッセージ定義 ディスパッチ: メッセージのディスパッチャ モデル:
アプリのデータモデル定義 ストア: アプリ全体のデータストア ビュー: 最上位コンポーネント(概念を追加) ハンドラー: 振る舞い実装(概念を追加)
1 方向に回す 1 方向に回す
アクション アクション 1 アクションに 1 振る舞い 振る舞い予約の抽象的概念 振る舞いに渡すパラメータ定義
ディスパッチ ディスパッチ アクションの送信と受信を繋ぐ 送信側と受信側を疎結合にする 定型処理
モデル モデル アプリで保有するデータ構造定義 概ね JSON タグをつけて運用 バックエンドと共通で利用できる
ストア ストア モデル定義に沿ったデータを保持 ロードされる JS と同じライフサイクル アプリ要件で永続化と復元機能が必要 ぶっちゃけただのグローバル変数 API 結果のキャッシュのようなもの
GopherJS はシングルスレッド動作のみなので 基本は排他処理等は気にしない
ビュー ビュー コンポーネントの性質 HTML の組み合わせを抽象化 プロパティを持ち、それを元に描画を決定 DOM イベントをアクションに翻訳する ビューの性質 Body
を根とするコンポーネントツリー ルーターで URL と 1 対 1 に対応
ハンドラー ハンドラー アクションに対応する挙動実装群 ストアを書き換えしてもいい唯一の存在 サンプルではストアパッケージに書いてる
ルーターの例 ルーターの例
ルーターの実装例 ルーターの実装例 type NormalView struct { vecty.Core Title string `vecty:"prop"`
Next string `vecty:"prop"` } func (c *NormalView) Render() vecty.ComponentOrHTML { vecty.SetTitle(c.Title) return elem.Body( elem.Heading1(vecty.Text(c.Title)), elem.Anchor( vecty.Markup(prop.Href("#/"+c.Next)), vecty.Text("link:"+c.Next), ), elem.Button( vecty Markup(
ルーターの実行例 ルーターの実行例 top link:next1 click
中規模な実装例 中規模な実装例 Bootstrap4 と API 呼び出し
フォルダ構成 assets/ スタティックファイル群 models.go データスキーマ components.go コンポーネント定義群 views.go ビュー定義群 main.go
メイン実装
models.go:Post models.go:Post // Post https://jsonplaceholder.typicode.com/posts type Post struct { UserID
int `json:"userId"` ID int `json:"id"` Title string `json:"title"` Body string `json:"body"` } type Posts []*Post
components.go:Post components.go:Post type PostInfo struct { vecty.Core Post Post `vecty:"prop"`
} func (c *PostInfo) Render() vecty.ComponentOrHTML { return elem.Div(vecty.Markup(vecty.Class("card")), elem.Div(vecty.Markup(vecty.Class("card-body")), elem.Heading5(vecty.Text(c.Post.Title)), elem.Paragraph(vecty.Text(c.Post.Body)), ), ) } type PostInfoList struct { vecty Core
views.go:/posts views.go:/posts type PostsView struct { vecty.Core Posts Posts `vecty:"prop"`
} func (c *PostsView) Render() vecty.ComponentOrHTML { vecty.SetTitle("Posts") return &Layout{ Children: vecty.List{ elem.UnorderedList( vecty.Markup( vecty.Class("nav"), vecty.Class("nav-tabs"), ), elem.ListItem( vecty Markup(vecty Class("nav-item"))
main.go main.go const API = "https://jsonplaceholder.typicode.com" func getResource(url string, v
interface{}) error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() return json.NewDecoder(resp.Body).Decode(&v) } func main() { vecty.AddStylesheet("assets/bootstrap.min.css") vecty.AddStylesheet("assets/app.css") r := router.New() r ForceHashURL = true
中規模な動作例 中規模な動作例 Users Posts Leanne Graham
[email protected]
Ervin Howell
[email protected]
Clementine Bauch
[email protected]
Patricia Lebsack
[email protected]
Chelsey Dietrich
[email protected]
Mrs. Dennis Schulist
[email protected]
Kurtis Weissnat
[email protected]
Nicholas Runolfsdottir V
[email protected]
QR コードコンポーネント QR コードコンポーネント
QR コード実装例 QR コード実装例 package components import ( "bytes" "log"
"github.com/gopherjs/vecty" "github.com/gopherjs/vecty/elem" "github.com/aaronarduino/goqrsvg" "github.com/ajstarks/svgo" "github.com/boombuler/barcode/qr" ) // QRCode ... type QRCode struct {
QR コード動作例 QR コード動作例 QR Code Text: WIFI:S:<SSID>; T:<WPA|WEP|>; P:<password>;;
スゴイところ スゴイところ JS の QR ライブラリつかってない。 JS を意識していない Go のライブラリを
GopherJS から利用しているだけ。
GopherJS 開発ツール GopherJS 開発ツール 「gopherjs serve <パッケージパス>」が基本 あとは Go ソースを書き換えた状態で
「 」にアクセスするだけで 再構築した js がサーブされます。 index.html があればそれをサーブします。 存在しない場合はミニマムなものがサーブされます。 http://localhost:8080
デバッグ デバッグ もちろんデバッグもできます。 Go のソースコードで!
ビルド ビルド 「-m」にてミニファイした結果が出力されます。 $ gopherjs build -m -o app.js .
チャットアプリの実際 チャットアプリの実際 https://github.com/nobonobo/vecty-sample
<リポジトリルート> app/ フロントパッケージルート backend/ バックエンドルート main.go サーバー起動実装
開発時のサーバー 開発時のサーバー gopherjs serve を別途起動しておく /api/をバックエンドハンドラにマップ その他を「gopherjs serve」にリバースプロキシ func main()
{ u, _ := url.Parse("http://localhost:8080") rp := httputil.NewSingleHostReverseProxy(u) http.Handle("/", rp) http.Handle("/api/", backend.New()) l, err := net.Listen("tcp", ":8888") if err != nil { log.Fatalln(err) } log.Println("listen:", l.Addr()) if err := http.Serve(l, nil); err != nil { log.Fatalln(err) } }
<フロントパッケージルート> vendor/ その他の Go 依存パッケージ actions/ アクション定義群 assets/ 静的ファイル群 components/
コンポーネントセット dispather/ ディスパッチャ実装 models/ アプリモデル定義群 store/ アプリストレージ実装 router/ ルーター実装 views/ アプリ向けビューセット main.go メイン実装 index.html アプリ HTML
チャットアプリ動作例 チャットアプリ動作例 Top dead‑simple chap app Create New Room
パッケージが分かれてると パッケージが分かれてると 追加するモデルやストア 追加するアクション 追加する静的ファイル 追加するコンポーネント 追加するビュー 追加先に迷わなくて済みます。
パッケージ間依存 パッケージ間依存 store -> models actions -> models, store components
-> models, dispatcher views -> store, components 振る舞いと見た目とアプリデータが 疎結合
フロントのデプロイ フロントのデプロイ 以下のファイル群を静的にサーブ assets/ index.html app.js 内容 必要 追加 <html>
<head> <meta charset="utf-8"> <script src="app.js"></script> </head> <body> </body> </html>
ロード時間の解決案(1) ロード時間の解決案(1) スピナー表示のあとで async 付きで JS を読む。 <html> <head> <meta
charset="utf-8"> <link href="assets/app.css" media="all" rel="stylesheet" /> </head> <body> <div class="loader">Loading...</div> </body> <script async src="app.js"></script> </html>
ロード時間の解決案(2) ロード時間の解決案(2) 先日公開されたサービス GopherJS パッケージを CDN キャッシュ jsgo.io https://github.com/dave/jsgo
Vecty の Pros/Cons Vecty の Pros/Cons
Pros Pros 基本の知識は HTML/CSS/Go のみで OK API やモデル定義をクラサバで共有できる 成果物は ES5
相当で polyfill 不要 豊富な PureGo のライブラリが利用可能 型のある開発は素晴らしい Vecty 本体は small & clear なので読みやすい
Cons Cons 結局 DOM まわりの知識は必要 DOM まわりを Go で置き換えるのに慣れが必要 JS
の既存 lib 使うならやっぱり JS 知識が必要 成果物 JS のファイルサイズがやや大きめ 開発中でもパッキングするのでリロード重い コンポーネントの設計はやはり難しい Vecty 安定板までに破壊的変更の予定がある
まとめ まとめ
とにかくビルドが早い とにかくビルドが早い 型チェック トランスパイル ミニファイ パック 上記処理のほとんどを1度のパースで行う。
JS の Isomorphic 開発 JS の Isomorphic 開発 nodejs/yarn webpack/esm/babel
eslint/prettier/flowtype mocha/react か vue.js etc... それぞれ要素技術ごとに薄い本ができるツ それぞれ要素技術ごとに薄い本ができるツ ラミ ラミ
Go の Isomorphic 開発 Go の Isomorphic 開発 go: 依存/整形/型検査/Lint/テスト/バックエンド
gopherjs: 開発サーバー/ES5/ミニファイ/パック vecty: 仮想 DOM フレームワーク たったこれだけで始められる たったこれだけで始められる Go に慣れた人が新たに覚える事は非常に少ない Go に慣れた人が新たに覚える事は非常に少ない
いろんな資産が使える いろんな資産が使える PureGo の資産はそのまま GopherJS に JS の資産はラップすれば GopherJS に
GopherJS の資産は GopherJS に GopherJS は GopherJS はジャイアニズム ジャイアニズム
しなくていいこと しなくていいこと 毎年追加される言語仕様を追う モジュール機構の選択 依存ツールメジャーバージョンアップ追従 コード整形ツールの調整 ツール間の競合機能の調整 バージョンの不整合の調整
GopherJS+Vecty GopherJS+Vecty おすすめです おすすめです
応用 1 応用 1 面白そう pouchdb(js)/couchdb(ネイティブ)両対応 pouchdb 使うとオフライン動作し、 オンライン化した時にクラウド上の couchdb
に同期なんてことができる。 Firebase(mBaaS)のような環境を作れるかも? だれかやってみて! だれかやってみて! kivik(isomorphic)
応用 2 応用 2 gRPC サーバーをバックエンドにできる。 クラサバ結合部の設計は gRPC の IDL
で完結する。 だれかやってみて! だれかやってみて! gopherjs-grpc-websocket
質問? 質問?
おしまい おしまい