Publicité

Om Next ~React.jsを超えて

14 Mar 2017
Publicité

Contenu connexe

Publicité

Dernier(20)

Publicité

Om Next ~React.jsを超えて

  1. Om Next ~React.js を超えて 2017/03/14 nishi-shinju-clojure#1 @goronao ハッシュタグ : #nishi-shinju-clojure
  2. 自己紹介 • @goronao • Clojure / Script 歴約二年 • 好きな Project : component / duct / om / reagent • 最近は job-streamer の保守開発をやってます https://github.com/job-streamer
  3. Om : React.js の ClojureScript ラッパー +α(om-0.9) Om Next : Om の後継 (om-1.0) • 今日はこの流れを前提に Om Next がどの様に React.js を超えているか話します ※ React.js 自体や周辺の JS ライブラリには触れますが詳述は しません(出来ません) Om Next とは React.js ➡ React.js ➡ cljs+α cljc+α+α React.js Om Om Next
  4. Om Next とは • Om Next の主要な仕様 • UI • グローバル状態管理 • クライアントサーバアーキテクチャ • サーバサイドサポート • Reconciler
  5. UI • Om Next における React Component 定義 • om.next/defui というマクロで定義する • React Component + メタデータ
  6. UI • 赤枠内が React.js のrender 実装 • その他 lifecycle methods(componentDidMount等)も実装可能
  7. UI • 赤枠内は後述 • React Component 以外に描画データ用のメタ情報を定義できる
  8. UI • これは単純に React.js のラッパーとしての機能 • React Component のデータ管理に関する問題 • React Component は2種類のデータを持つ • Props : 親から受け継がれる Immutable なデータ • State : Component 自体が保持する Mutable なデータ 更新が再描画の条件となる • State(状態)が複雑性の原因 • React Component それぞれが状態を持つため管理が難しい • 全体の情報を参照できない • 自分以外の Component の状態更新
  9. UI State State State State State State Props Props Props Props Props Components 描画
  10. UI State State State State State State Props Props Props Props Props update Components
  11. UI State State State State State State Props Props Props Props Props update Components 再描画
  12. UI State State State State State State Props Props Props Props Props update Components 再描画
  13. グローバル状態管理 • 状態をアプリケーション全体で1つだけ持つ • グローバル状態はツリー構造を取りこれを参照・更新 • Component はPropsで受け渡されるデータを描画すればよいだけ • 状態の更新に伴い Component ツリー上の対応箇所を再描画 • React.js の場合 • Redux を組み合わせて実現 • Om(旧) の場合 • グローバル状態管理をサポート • 再描画する Component を決定するために、状態と Component ツ リーの対応を定義する cursors という仕組みも導入
  14. グローバル状態管理(Om) Props Props Props Props Props Components State cursors により対応 Props cursor cursor cursor cursor cursor cursor
  15. Props Props Props Props Props Components State PropsMount 描画 グローバル状態管理(Om)
  16. Props Props Props Props Props Components State Props グローバル状態管理(Om) cursors によって対応 transact! cursor
  17. Props Props Props Props Props Components State 再描画 Props グローバル状態管理(Om) cursors によって対応 cursor transact!
  18. グローバル状態管理 • Om(旧) の場合 • グローバル状態の導入で状態管理はシンプルに • 状態と Component ツリーの対応関係を cursors で定義 • しかしグローバル状態と Component が密結合してしまう問題がある • cursor はグローバル状態のサブセットでありツリー構造を取る • UI はツリー構造の状態に紐づけられるとは限らない(むしろ稀) • Om Next の場合 • グローバル状態管理を同様にサポート • 次に触れるクライアントサーバアーキテクチャによって状態を Component から分離し上記の問題の解決を図る
  19. クライアントサーバアーキテクチャ 1. クライアントサイドにサーバ(の様なもの)を設ける • 必要な情報操作(Read, Mutate)はルーター(Parser)にクエリを発 行して対応する処理を実行 • 直接 Component から状態を操作しないため両者の分離が図れる parser定義(実態は multimethod) read, mutate クエリ
  20. クライアントサーバアーキテクチャ • Read クエリは UI に定義する • View に全く関係ないデータは取る意味ない • グローバル状態からの単純な抜き出しなら Parser の実装不要
  21. クライアントサーバアーキテクチャ Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query
  22. クライアントサーバアーキテクチャ Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query
  23. クライアントサーバアーキテクチャ Props Props Props Props Props Components State Query Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query Props Read :products/list :products/cart :products/purchase
  24. クライアントサーバアーキテクチャ 2. グローバル状態をグラフ構造で表現する • グラフ構造を表現するためにはデータの一意性の定義が必要 • これも UI に定義する • (下記例) product は :product/number で一意
  25. クライアントサーバアーキテクチャ • 一意性定義を基に Om Next はグローバル状態をグラフ構造に ノーマライズ • (下記例) name で一意にノーマライズ
  26. クライアントサーバアーキテクチャ • 以上 により Component と状態を分離した上で、 グラフ構造の状態と Component ツリーの対応を表現できる • cursors の状態サブセットによる対応ではなく、 クエリによる状態参照でグラフと Component が紐づく • UI (Component) はクエリと一意性定義を持つ ... ① • クエリも UI (Component) への参照を持つ ... ② ① ②
  27. クライアントサーバアーキテクチャ Props Props Props Props Props Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query Props transact!
  28. クライアントサーバアーキテクチャ Props Props Props Props Props Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query Props
  29. クライアントサーバアーキテクチャ Props Props Props Props Props Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query Props 再描画
  30. クライアントサーバアーキテクチャ • React.js の場合 • Redux / GraphQL あたりまで組み合わせて実現(ざっくり) • GraphQL も参照型を持ちグラフ構造が表現可能 • 通常、GraphQL はサーバサイドとの通信に用いる • Om Next の場合 • サーバサイドもサポートする
  31. サーバサイドサポート • サーバサイド Parser • クライアントサイドと同じ構文でサーバサイドにも Parser を定義可能 • 単一のエンドポイントで公開 • エンドポイントの呼び出し定義と、サーバサイド Parser を呼び出す ルートの定義をする(:remoteキーを含んだマップを返すだけ)
  32. サーバサイドサポート Components State Read :products/list ➡ {:remote true} :products/cart :products/purchase Parser(Client Side) Mutate 'cart/add-product 'cart/remove-product ‘products/purchase ➡ {:remote true} Query Query Query Read :products/list Mutate 'products/purchase Parser(Server Side) transact!
  33. サーバサイドサポート Components StateParser(Client Side) Mutate 'cart/add-product 'cart/remove-product ‘products/purchase ➡ {:remote true} Query Query Query Read :products/list Mutate 'products/purchase Parser(Server Side) Props Read :products/list ➡ {:remote true} :products/cart :products/purchase 自動マージ
  34. サーバサイドサポート • Om Next の場合 • サーバサイドのRead クエリ結果はグローバル状態へ自動マージ • Parser とグローバル状態が一元管理されているため、 遅延読み込みや楽観的更新も簡単に実現可能 • React.js の場合 • Redux / GraphQL / Relay を組み合わせて実現(ざっくり) • 当然サーバサイド (Node.jsなど) も別に用意が必要
  35. Reconciler • 紹介してきた機能を実現するためには全体の統合が必要 • Om Next でそれを担っているのが Reconciler • 諸々の定義を渡して生成 • グローバル状態、parser、サーバとの通信方法 • Component ツリー構築時に渡すと後はよきに計らってくれる
  36. Query Server Reconciler Components State Read Reconciler Mutate Read /api/query Mutate
  37. React.js / Om / Om Next 比較 • Om Next ~ React.js + Redux + GraphQL + Relay + Node.js? • 組み合わせを選択しなければならないのはそれ自体 React.js の Cons ではある(Falcor vs. GraphQL でも同じような議論) React.js Om Om Next UI - React.js 同等 React.js 同等 グローバル状態管理 なし (+Redux が必要) あり (ツリー構造) あり (グラフ構造) クライアントサーバ アーキテクチャ なし (+Redux / GraphQL が必要) なし あり サーバサイド サポート なし (+Redux / GraphQL / Relay / Node.js が必要) なし あり
  38. Om Next 独自機能 • React.js+周辺ライブラリで実現できない機能も存在する • JVM でサーバサイドレンダリング • 俺達には cljc がある! • ui, parser, reconciler いずれもサーバサイド(JVM)上に実装可能 https://designudge.org/ja/stories/1481369485901_fe99b3b7/ • 描画効率の改善 • Incremental Rendering • Reconciler が状態とComponentの関係を全て把握(Indexer) しているためより効率的な再描画が可能 https://anmonteiro.com/2016/09/om-next-internals-incremental- rendering/
  39. Incremental Rendering Props Props Props Props Props Components State Query Read :products/list :products/cart :products/purchase Parser Mutate 'cart/add-product 'cart/remove-product 'products/purchase QueryQuery Query Query Query Props 再描画
  40. Om Next Cons • Clojurian 以外へのハードル爆上げ • 学習曲線が急すぎる • 機能多すぎる • GraphQL, Relay 等を先に学習しないとアーキテクチャの理解が厳しい • ロードマップが不明 • もうコンセプトにぶれはなさそう • Circle CI で使ってるし... • 状態管理方式の選択 • Component 状態は閉じられているわけではないのでアーキテクチャを 理解しないとこれまで紹介した問題は依然発生する
  41. Om Next の採用シーン • クライアント側の状態管理が複雑なアプリケーション • 多様なソース(サーバからのデータ、クライアントオンリーデータ、 Web Storageに保存されたデータ)からのデータを扱う • それぞれの更新も頻繁に発生 • Om Next では Reconciler が上手く統合してくれる • CircleCI はまさにこれ • UI の変更が多いアプリケーション • Om Next は Component 自身が自分の必要とするデータを宣言してい るため変更に強い • (既存の Clojure / Script 資産がある場合...)
  42. まとめ • Om Next は二つの意味で React.js を超えていると言える • React.js 単体で実現できない機能(Redux, GraphQL, Relayの責務) をサポート • 更にそれらの組み合わせでも実現できない魅力的な機能が存在 • 採用にやや不安はあるものの、最新のフロントエンド界隈の知 識が得られるため学んでおいて損はない Try Om Next!
  43. ありがとうございました
Publicité