Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
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
680
TinyGoでIoTを始めよう
nobonobo
2
950
Goだけでロビーサーバーを作ってみた
nobonobo
2
590
Other Decks in Technology
See All in Technology
形式手法を使って仕様をコーディングしよう
mikanichinose
0
180
AI時代に必要な脅威モデリング― 変化の速度に負けない理解とリスク判断
masakane55
4
1.1k
1万名の社員が使う認証基盤で どう信頼性を担保するか?
kairim0
0
210
購入ドメインでの課題と取り組み
ykagano
0
140
【データ横丁主催】AI Agentがコンテキストを使って仕事をした後、何が残るのか― 組織の経験を次の判断に引き継ぐ「Agent Memory」
shisyu_gaku
2
330
Meet AgentCore Identity Consent Portal
hironobuiga
3
190
おそらく日本で唯一のDevRelインターン生として
husengs7
0
170
AI 時代の Azure エンジニアリング ~ 私たちは何を磨き、何を任せるのか ~
chack411
1
480
ビジネスを止めない技術的負債の返済のための戦略とその手法 - 技術的負債と向き合う / Complexity and Simplicity
soudai
PRO
3
630
AIに攻撃される前に、AIに攻撃させる
tsuchikazu
0
150
HacobuにおけるFDEとは/登壇資料(戸井田 裕貴)
hacobu
PRO
1
780
AIアプリを使い分けるための共通ツール基盤を考えてみる#devio2026
osawa
0
260
Featured
See All Featured
How STYLIGHT went responsive
nonsquared
100
6.3k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
600
Accessibility Awareness
sabderemane
1
230
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.7k
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
480
Embracing the Ebb and Flow
colly
88
5.2k
Darren the Foodie - Storyboard
khoart
PRO
4
4k
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
290
Color Theory Basics | Prateek | Gurzu
gurzu
1
480
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
133
20k
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
質問? 質問?
おしまい おしまい