om next ~react.jsを超えて

Post on 11-Apr-2017

329 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Om Next ~React.js を超えて

2017/03/14

nishi-shinju-clojure#1

@goronao

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

自己紹介

• @goronao

• Clojure / Script 歴約二年

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

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

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

Om Next とは

• Om Next の主要な仕様• UI

• グローバル状態管理

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

• サーバサイドサポート

• Reconciler

UI

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

• React Component + メタデータ

UI

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

UI

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

UI

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

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

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

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

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

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

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

UI

State

State

State State State

State

Props

Props

Props

Props Props

Components

描画

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

再描画

UI

State

State

State State State

State

Props

Props

Props

Props Props

update

Components

再描画

グローバル状態管理

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

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

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

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

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

Props

Props

Props

Props Props

Components State

cursorsにより対応

Props

cursor cursor cursor

cursor cursor

cursor

Props

Props

Props

Props Props

Components State

PropsMount描画

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

Props

Props

Props

Props Props

Components State

Props

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

cursorsによって対応

transact!

cursor

Props

Props

Props

Props Props

Components State

再描画Props

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

cursorsによって対応

cursor

transact!

グローバル状態管理

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Query

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

Parser

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

QueryQuery

Query Query

Query

クライアントサーバアーキテクチャ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

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

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

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

• これも UI に定義する

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

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

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

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

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

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

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

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

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

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!

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

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

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

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

再描画

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

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

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

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

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

サーバサイドサポート

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

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

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

サーバサイドサポート

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!

サーバサイドサポート

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

自動マージ

サーバサイドサポート

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

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

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

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

Reconciler

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

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

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

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

Query ServerReconciler

Components State

Read

Reconciler

Mutate

Read

/api/query

Mutate

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

なし あり

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/

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

再描画

Om Next Cons

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

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

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

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

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

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

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

Om Next の採用シーン

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

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

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

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

• CircleCI はまさにこれ

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

るため変更に強い

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

まとめ

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

をサポート

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

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

Try Om Next!

ありがとうございました

top related