om next ~react.jsを超えて

43
Om Next ~React.js を超えて 2017/03/14 nishi-shinju-clojure#1 @goronao ハッシュタグ : #nishi-shinju-clojure

Upload: kazuki-tsutsumi

Post on 11-Apr-2017

330 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Om Next ~React.jsを超えて

Om Next ~React.js を超えて

2017/03/14

nishi-shinju-clojure#1

@goronao

ハッシュタグ : #nishi-shinju-clojure

Page 2: Om Next ~React.jsを超えて

自己紹介

• @goronao

• Clojure / Script 歴約二年

• 好きな Project : component / duct / om / reagent

• 最近は job-streamer の保守開発をやってますhttps://github.com/job-streamer

Page 3: Om Next ~React.jsを超えて

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

Page 4: Om Next ~React.jsを超えて

Om Next とは

• Om Next の主要な仕様• UI

• グローバル状態管理

• クライアントサーバアーキテクチャ

• サーバサイドサポート

• Reconciler

Page 5: Om Next ~React.jsを超えて

UI

• Om Next における React Component 定義• om.next/defui というマクロで定義する

• React Component + メタデータ

Page 6: Om Next ~React.jsを超えて

UI

• 赤枠内が React.js のrender 実装• その他 lifecycle methods(componentDidMount等)も実装可能

Page 7: Om Next ~React.jsを超えて

UI

• 赤枠内は後述• React Component 以外に描画データ用のメタ情報を定義できる

Page 8: Om Next ~React.jsを超えて

UI

• これは単純に React.js のラッパーとしての機能

• React Component のデータ管理に関する問題• React Component は2種類のデータを持つ

• Props : 親から受け継がれる Immutable なデータ

• State : Component 自体が保持する Mutable なデータ更新が再描画の条件となる

• State(状態)が複雑性の原因• React Component それぞれが状態を持つため管理が難しい

• 全体の情報を参照できない

• 自分以外の Component の状態更新

Page 9: Om Next ~React.jsを超えて

UI

State

State

State State State

State

Props

Props

Props

Props Props

Components

描画

Page 10: Om Next ~React.jsを超えて

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

Page 11: Om Next ~React.jsを超えて

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

再描画

Page 12: Om Next ~React.jsを超えて

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

再描画

Page 13: Om Next ~React.jsを超えて

グローバル状態管理

• 状態をアプリケーション全体で1つだけ持つ• グローバル状態はツリー構造を取りこれを参照・更新• Component はPropsで受け渡されるデータを描画すればよいだけ• 状態の更新に伴い Component ツリー上の対応箇所を再描画

• React.js の場合• Redux を組み合わせて実現

• Om(旧) の場合• グローバル状態管理をサポート• 再描画する Component を決定するために、状態と Component ツ

リーの対応を定義する cursors という仕組みも導入

Page 14: Om Next ~React.jsを超えて

グローバル状態管理(Om)

Props

Props

Props

Props Props

Components State

cursorsにより対応

Props

cursor cursor cursor

cursor cursor

cursor

Page 15: Om Next ~React.jsを超えて

Props

Props

Props

Props Props

Components State

PropsMount描画

グローバル状態管理(Om)

Page 16: Om Next ~React.jsを超えて

Props

Props

Props

Props Props

Components State

Props

グローバル状態管理(Om)

cursorsによって対応

transact!

cursor

Page 17: Om Next ~React.jsを超えて

Props

Props

Props

Props Props

Components State

再描画Props

グローバル状態管理(Om)

cursorsによって対応

cursor

transact!

Page 18: Om Next ~React.jsを超えて

グローバル状態管理

• Om(旧) の場合• グローバル状態の導入で状態管理はシンプルに

• 状態と Component ツリーの対応関係を cursors で定義

• しかしグローバル状態と Component が密結合してしまう問題がある• cursor はグローバル状態のサブセットでありツリー構造を取る

• UI はツリー構造の状態に紐づけられるとは限らない(むしろ稀)

• Om Next の場合• グローバル状態管理を同様にサポート

• 次に触れるクライアントサーバアーキテクチャによって状態をComponent から分離し上記の問題の解決を図る

Page 19: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

1. クライアントサイドにサーバ(の様なもの)を設ける• 必要な情報操作(Read, Mutate)はルーター(Parser)にクエリを発

行して対応する処理を実行

• 直接 Component から状態を操作しないため両者の分離が図れる

parser定義(実態は multimethod) read, mutate クエリ

Page 20: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

• Read クエリは UI に定義する• View に全く関係ないデータは取る意味ない

• グローバル状態からの単純な抜き出しなら Parser の実装不要

Page 21: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャComponents State

Query

Read:products/list:products/cart:products/purchase

Parser

Mutate'cart/add-product'cart/remove-product'products/purchase

QueryQuery

Query Query

Query

Page 22: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャComponents State

Query

Read:products/list:products/cart:products/purchase

Parser

Mutate'cart/add-product'cart/remove-product'products/purchase

QueryQuery

Query Query

Query

Page 23: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

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

Page 24: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

2. グローバル状態をグラフ構造で表現する• グラフ構造を表現するためにはデータの一意性の定義が必要

• これも UI に定義する

• (下記例) product は :product/number で一意

Page 25: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

• 一意性定義を基に Om Next はグローバル状態をグラフ構造にノーマライズ• (下記例) name で一意にノーマライズ

Page 26: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

• 以上 により Component と状態を分離した上で、グラフ構造の状態と Component ツリーの対応を表現できる• cursors の状態サブセットによる対応ではなく、

クエリによる状態参照でグラフと Component が紐づく

• UI (Component) はクエリと一意性定義を持つ ... ①

• クエリも UI (Component) への参照を持つ ... ②

Page 27: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

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!

Page 28: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

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

Page 29: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

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

再描画

Page 30: Om Next ~React.jsを超えて

クライアントサーバアーキテクチャ

• React.js の場合• Redux / GraphQL あたりまで組み合わせて実現(ざっくり)

• GraphQL も参照型を持ちグラフ構造が表現可能

• 通常、GraphQL はサーバサイドとの通信に用いる

• Om Next の場合• サーバサイドもサポートする

Page 31: Om Next ~React.jsを超えて

サーバサイドサポート

• サーバサイド Parser• クライアントサイドと同じ構文でサーバサイドにも Parser を定義可能

• 単一のエンドポイントで公開

• エンドポイントの呼び出し定義と、サーバサイド Parser を呼び出すルートの定義をする(:remoteキーを含んだマップを返すだけ)

Page 32: Om Next ~React.jsを超えて

サーバサイドサポート

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!

Page 33: Om Next ~React.jsを超えて

サーバサイドサポート

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

自動マージ

Page 34: Om Next ~React.jsを超えて

サーバサイドサポート

• Om Next の場合• サーバサイドのRead クエリ結果はグローバル状態へ自動マージ

• Parser とグローバル状態が一元管理されているため、遅延読み込みや楽観的更新も簡単に実現可能

• React.js の場合• Redux / GraphQL / Relay を組み合わせて実現(ざっくり)

• 当然サーバサイド (Node.jsなど) も別に用意が必要

Page 35: Om Next ~React.jsを超えて

Reconciler

• 紹介してきた機能を実現するためには全体の統合が必要

• Om Next でそれを担っているのが Reconciler• 諸々の定義を渡して生成

• グローバル状態、parser、サーバとの通信方法

• Component ツリー構築時に渡すと後はよきに計らってくれる

Page 36: Om Next ~React.jsを超えて

Query ServerReconciler

Components State

Read

Reconciler

Mutate

Read

/api/query

Mutate

Page 37: Om Next ~React.jsを超えて

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 が必要)

なし あり

Page 38: Om Next ~React.jsを超えて

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/

Page 39: Om Next ~React.jsを超えて

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

再描画

Page 40: Om Next ~React.jsを超えて

Om Next Cons

• Clojurian 以外へのハードル爆上げ

• 学習曲線が急すぎる• 機能多すぎる

• GraphQL, Relay 等を先に学習しないとアーキテクチャの理解が厳しい

• ロードマップが不明• もうコンセプトにぶれはなさそう

• Circle CI で使ってるし...

• 状態管理方式の選択• Component 状態は閉じられているわけではないのでアーキテクチャを

理解しないとこれまで紹介した問題は依然発生する

Page 41: Om Next ~React.jsを超えて

Om Next の採用シーン

• クライアント側の状態管理が複雑なアプリケーション• 多様なソース(サーバからのデータ、クライアントオンリーデータ、

Web Storageに保存されたデータ)からのデータを扱う

• それぞれの更新も頻繁に発生

• Om Next では Reconciler が上手く統合してくれる

• CircleCI はまさにこれ

• UI の変更が多いアプリケーション• Om Next は Component 自身が自分の必要とするデータを宣言してい

るため変更に強い

• (既存の Clojure / Script 資産がある場合...)

Page 42: Om Next ~React.jsを超えて

まとめ

• Om Next は二つの意味で React.js を超えていると言える• React.js 単体で実現できない機能(Redux, GraphQL, Relayの責務)

をサポート

• 更にそれらの組み合わせでも実現できない魅力的な機能が存在

• 採用にやや不安はあるものの、最新のフロントエンド界隈の知識が得られるため学んでおいて損はない

Try Om Next!

Page 43: Om Next ~React.jsを超えて

ありがとうございました