Go + WASMで始めるWeb Frontend開発 / Web frontend development using Go and WebAssembly

5e511bb6d01d856b4ec7a0e5b1f6a2f0?s=47 syumai
December 14, 2018

Go + WASMで始めるWeb Frontend開発 / Web frontend development using Go and WebAssembly

5e511bb6d01d856b4ec7a0e5b1f6a2f0?s=128

syumai

December 14, 2018
Tweet

Transcript

  1. Go + WASM で始めるWeb Frontend 開発 Emscripten & WebAssembly night

    !! #6 2018/12/14 (Fri.) @__syumai
  2. 自己紹介 syumai Twitter: @__syumai Merpay, Inc. (Backend / Go) 前職ではFrontend

    やってました React Native Vue.js etc...
  3. 本日のお題 Go とWebAssembly でWeb Frontend の開発をする

  4. Go とWebAssembly

  5. Go 1.11 でWASM サポー トが追加 8 月末頃Go 1.11 がリリー スされる

    その際、Experimental な機能としてWASM サポー トが入る オプション付きでビルドするとWASM として吐き出される
  6. WASM ビルド用のコマンド G O O S = j s G

    O A R C H = w a s m g o b u i l d - o m a i n . w a s m G O O S = j s , G O A R C H = w a s m を指定 G O O S , G O A R C H はマルチプラットフォー ム向けの書き出しオ プション
  7. Go のWASM の実行 Go 本家制作の w a s m _

    e x e c . j s をロー ドしてから実行 < s c r i p t s r c = " w a s m _ e x e c . j s " > < / s c r i p t > < s c r i p t > ( a s y n c ( ) = > { c o n s t g o = n e w G o ( ) ; c o n s t { i n s t a n c e } = a w a i t W e b A s s e m b l y . i n s t a n t i a t e S t r e a m i n g ( f e t c h ( " m a i n . w a s m " ) , g o . i m p o r t O b j e c t ) g o . r u n ( i n s t a n c e ) ; } ) ( ) ; < / s c r i p t >
  8. Go からJavaScript を呼ぶ

  9. syscall/js pacakge Go からJS の世界を触りに行くためのpackage https://golang.org/pkg/syscall/js/

  10. syscall/js の基本的な使い方 / / w i n d o w

    を取ってくる w i n d o w : = j s . G l o b a l ( ) / / d o c u m e n t を取ってくる d o c u m e n t : = w i n d o w . G e t ( " d o c u m e n t " ) / / 任意のD O M を取ってくる b t n : = d o c u m e n t . C a l l ( " g e t E l e m e n t B y I d " , " b u t t o n " ) / / E v e n t L i s t e r n e r を設定する b t n . C a l l ( " a d d E v e n t L i s t e n e r " , " c l i c k " , j s . N e w C a l l b a c k ( f u n c ( [ ] j s . V a l u e ) { p r i n t l n ( " H e l l o , w o r l d ! " ) } ) , )
  11. 使い方の基本的なルー ル JS の世界の値は、 j s . V a l

    u e 型で渡ってくる! ( 具象型です) 値がObject であれば、 o b j . G e t ( " k e y " ) , o b j . S e t ( " k e y " , " v a l u e " ) が使える global なObject は、 j s . G l o b a l ( ) で取れる
  12. Go の値をJS の値として渡す js.ValueOf() で変換する js.ValueOf(hoge) でjs.Value 型の値が取れる []interface{} =>

    Array map[string]interface{} => Object js.Callback => function など j s . V a l u e . S e t ( ) を使うと、 自動で j s . V a l u e O f ( ) を使い値を変 換してセットしてくれるので楽です
  13. Go => JS の値の変換表 Go JavaScript js.Callback function nil null

    bool boolean integers and floats number string string []interface{} Array map[string]interface{} Object
  14. 実際に作ったもの

  15. go‑wasm‑todo‑list デモ https://syumai.github.io/go‑wasm‑todo‑list/

  16. 実装について

  17. State p a c k a g e t o

    d o t y p e S t a t e s t r u c t { l a s t I D i n t t o D o s [ ] T o D o D a t a } v a r s t a t e = S t a t e { } f u n c a p p e n d T o D o ( t i t l e s t r i n g ) { s t a t e . l a s t I D + + s t a t e . t o D o s = a p p e n d ( s t a t e . t o D o s , T o D o D a t a { T i t l e : t i t l e , I D : s t a t e . l a s t I D , } ) }
  18. View f u n c T o D o (

    p r o p s h . O b j e c t ) h . V N o d e { d o i n g T o D o s , d o n e T o D o s : = s e p a r a t e T o D o s ( s t a t e . t o D o s ) r e t u r n h . H ( " d i v " , n i l , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t C o n t a i n e r S t y l e } , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t S t y l e } , h . H ( " h 3 " , n i l , h . T e x t ( " D o i n g " ) ) , h . H ( " d i v " , n i l , h . T e x t ( " C o u n t : " + s t r c o n v . I t o a ( l e n ( d o i n g h . H ( t o D o L i s t , h . O b j e c t { " t o D o s " : d o i n g T o D o s } ) , ) , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t S t y l e } , h . H ( " h 3 " , n i l , h . T e x t ( " D o n e " ) ) , h . H ( " d i v " , n i l , h . T e x t ( " C o u n t : " + s t r c o n v . I t o a ( l e n ( d o n e T h . H ( t o D o L i s t , h . O b j e c t { " t o D o s " : d o n e T o D o s } ) , ) , ) , ) }
  19. App f u n c m a i n (

    ) { a p p : = f u n c ( o b j e c t h . O b j e c t ) h . V N o d e { r e t u r n h . H ( " d i v " , n i l , h . H ( " h 2 " , n i l , h . T e x t ( " T o D o L i s t " ) ) , h . H ( t o d o . T o D o , n i l ) , h . H ( " a " , h . O b j e c t { " h r e f " : " h t t p s : / / g i t h u b . c o m / s y u m a i / g o - w h . T e x t ( " S h o w t h e c o d e o n G i t H u b " ) , ) , ) } c o r e . S e t R o o t C o m p o n e n t ( a p p ) c o r e . U p d a t e ( ) s e l e c t { } }
  20. 別の書き方をすると… ※ このコー ドはイメー ジです f u n c m

    a i n ( ) { a p p : = f u n c ( o b j e c t h . O b j e c t ) h . V N o d e { r e t u r n ( < d i v > < h 2 > T o D o L i s t < / h 2 > < t o d o . T o D o / > < a h r e f = " h t t p s : / / g i t h u b . c o m / s y u m a i / g o - w a s m - t o d o - l i s t / " S h o w t h e c o d e o n G i t H u b < / a > < / d i v > ) } c o r e . S e t R o o t C o m p o n e n t ( a p p ) c o r e . U p d a t e ( ) s e l e c t { } }
  21. 何をやっているのか Hyperscript をGo 用に実装した Go の関数でHTML を表現できる 仕組みとしては、React やHyperapp などのJSX

    => Hyperscript 変換 を行っているフレー ムワー クと同じ 単一のState からDOM 全体を構成するアプリケー ションが作れる Go だけでリッチなFrontend アプリケー ションの実装が出来る!
  22. go‑hyperscript https://github.com/syumai/go‑hyperscript f u n c H ( t a

    g i n t e r f a c e { } , a t t r s O b j e c t , c h i l d r e n . . . V N o d e ) V N o d e { . . . } 第一引数にタグ名 (or Component) 第二引数に属性 第三引数以降は子のVNode を渡す 属性を受け取って、VNode を返す関数はComponent として扱える
  23. さっきのView f u n c T o D o (

    p r o p s h . O b j e c t ) h . V N o d e { d o i n g T o D o s , d o n e T o D o s : = s e p a r a t e T o D o s ( s t a t e . t o D o s ) r e t u r n h . H ( " d i v " , n i l , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t C o n t a i n e r S t y l e } , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t S t y l e } , h . H ( " h 3 " , n i l , h . T e x t ( " D o i n g " ) ) , h . H ( " d i v " , n i l , h . T e x t ( " C o u n t : " + s t r c o n v . I t o a ( l e n ( d o i n g h . H ( t o D o L i s t , h . O b j e c t { " t o D o s " : d o i n g T o D o s } ) , ) , h . H ( " d i v " , h . O b j e c t { " s t y l e " : l i s t S t y l e } , h . H ( " h 3 " , n i l , h . T e x t ( " D o n e " ) ) , h . H ( " d i v " , n i l , h . T e x t ( " C o u n t : " + s t r c o n v . I t o a ( l e n ( d o n e T h . H ( t o D o L i s t , h . O b j e c t { " t o D o s " : d o n e T o D o s } ) , ) , ) , ) }
  24. 困ったこと wasm のサイズがでかい minimum でも1.3MB、 複数package をimport すると10MB を超 えることも

    Gzip で圧縮すると20% 程度にはなります TinyGo ( 埋め込み用のGo 互換コンパイラ) でコンパイルすると 非常に小さいサイズ ( 数10KB~) になるらしい 試してみたもののまだ動かせてないです
  25. None
  26. 困ったこと テストが困難 syscall/js をimport しているとamd64 でコンパイル出来ない G O O S

    = j s G O A R C H = w a s m g o t e s t . . . しても動かない go‑hyperscript では、js.Value 相当のinterface を定義してtest 時 にはモックするようにしました Go 本体のsyscall/js にはtest が書いてあるので、 何かいい方法が あるのかも
  27. 今後の展望 現状、go‑hyperscript ではDOM の生成しか対応出来ていない UI の更新時は、DOM を丸っと消して再生成している… 汗 差分更新を実装中です! https://twitter.com/__syumai/status/1073283389975744512

  28. 最後に 言語の枠にとらわれないFrontend 開発を楽しくやっていきましょ う!

  29. ご清聴ありがとうございました!