Slide 1

Slide 1 text

React Compilerと Fine-Grained Reactivityと 宣言的UIのこれから JSConf JP 2024 - TOMIKAWA Sotaro (ssssota)

Slide 2

Slide 2 text

はじめまして! 冨川  宗太郎 TOMIKAWA Sotaro 株式会社ZOZO フロントエンドエンジニア(テックリード) 仕事ではReact、趣味はSvelteかPreactを使っていることが多いです。 { "x": "ssssotaro", "bsky": "ssssota.bsky.social", "github": "ssssota" }

Slide 3

Slide 3 text

きょうは宣言的UIのはなし 宣言的UIはWeb開発の標準となり、Webだけでなくモバイルアプリケーションや デスクトップアプリケーションにも広がりを見せている。 Web: React, Vue.js, Svelte, Preact, etc… モバイル: SwiftUI, Jetpack Compose, React Native, Flutter, etc… APIはそれぞれ少しずつ異なるものの、 「状態をもとにUIを宣言する」 という基本的な考え方は共通している。 今日はWeb開発における宣言的UIのこれまでとこれからを考える。

Slide 4

Slide 4 text

1章 宣言的UIと仮想DOM

Slide 5

Slide 5 text

宣言的UIと仮想DOM 10年前、“仮想DOMという概念が俺達の魂を震えさせ”ていた。 (仮想DOMは宣言的UIを実現するための手段で宣言的UIそのものではないが) 宣言的UIをここまで広めたのはReactやVue.jsのような仮想DOMを使ったラ イブラリの存在といっても過言ではない。 宣言的UIのこれからを語る上で、仮想DOMをまずは振り返る。

Slide 6

Slide 6 text

仮想DOMのしくみ 例えばこんなコンポーネントを考える。 function App({ name }) { return (

Hello {name}!

Welcome to the session.

); }

Slide 7

Slide 7 text

仮想DOMのしくみ 0. 初回レンダリング時の仮想DOM div h1 p Hello Everyone! Welcome to the session. {name: "Everyone"} 1. 状態変化時 仮想DOMを再構築する div h1 p Hello JSConf JP! Welcome to the session. {name: "JSConf JP"} 2. 仮想DOMが構築できたら、差分を検出する (reconciliation / diffing) 3. 検出した差分をもとに、実際のDOMに反映する (render, commit)

Slide 8

Slide 8 text

Virtual DOM is pure overhead 「仮想DOMは純粋なオーバーヘッドである」 Svelte作者のRich Harris氏が6年前に公開したブログのタイトル。 今後の宣言的UIを考える上での重要なキーワード。 1. 仮想DOMの差分検出自体コストがかかる 仮想DOMツリーを探索して、効率よく実際のDOMに適用するための差分 を検出する必要がある Reactでは のアルゴリズムを使っているとされる(コスト小) 2. 仮想DOMの構築自体コストがかかる 仮想DOMツリーの構築では様々な計算やアロケーションが何度も発生す る 各種配列、仮想DOM自体のオブジェクト、インライン関数、、、 O(n)

Slide 9

Slide 9 text

React Compiler 今年春発表されたReact Compiler (React Forgetは2021年発表)。 これが、先の問題を解決する。 2. 仮想DOMの構築自体コストがかかる 仮想DOMツリーの構築では様々な計算やアロケーションが何度も発生 する 各種配列、仮想DOM自体のオブジェクト、インライン関数、、、 様々な計算やアロケーションとは?どのように解決するのか?

Slide 10

Slide 10 text

例:仮想DOMオブジェクトの計算・アロケーションコスト削減 function App({ name }) { return

Hello {name}!

; } function App(t0) { const $ = _c(2); const { name } = t0; let t1; if ($[0] !== name) { t1 =

Hello {name}!

; $[0] = name; $[1] = t1; } else { t1 = $[1]; // name が同じ→ 再利用 } return t1; }

Slide 11

Slide 11 text

例:関数の計算・アロケーションコスト削減 function List({ items }) { return (
    {items.map((item) => { return
  • {item}
  • ; })}
); } function List(t0) { const $ = _c(4); const { items } = t0; let t1; if ($[0] !== items) { t1 = items.map(_temp); // . . . } // ↓ トップレベルへ移動 function _temp(item) { return
  • {item}
  • ; }

    Slide 12

    Slide 12 text

    React Compiler useMemo や React.memo を使えばできなくもないが、 開発者がそれを意識し なければならなかった。 これらのコストをReact Compilerが最適化する。 1. 仮想DOMオブジェクトをキャッシュする 2. インライン関数をトップレベルに移動する/キャッシュする 状態変化時、通常は状態が変化したコンポーネントの子孫も再構築されるが、 React Compilerでは再構築されるコンポーネントを最小限に抑える。

    Slide 13

    Slide 13 text

    仮想DOMのしくみ(with React Compiler) 0. 初回レンダリング時の仮想DOM div h1 p Hello Everyone! Welcome to the session. {name: "Everyone"} 1. 状態変化時 仮想DOMを再構築する div h1 p Hello JSConf JP! Welcome to the session. {name: "JSConf JP"} 2. 仮想DOMが構築できたら、差分を検出する (reconciliation / diffing) 3. 検出した差分をもとに、実際のDOMに反映する (render, commit)

    Slide 14

    Slide 14 text

    宣言的UIと仮想DOMとReact Compiler ここまで、ReactのReactによるReactのためのReact Compilerを使った Virtual DOM is pure overhead への対応を見てきた。 これは仮想DOMと 共存する道の1つと言える。 (仮想DOMのVue.js SFCもコンパイルを伴うため最適化が行われている) 一方で、仮想DOMを使わない宣言的UIも存在する。

    Slide 15

    Slide 15 text

    閑話休題 宣言的UIとJSX いまでは様々なフレームワークが利用しているJSX。 当初はFacebook(現Meta)がReactのために開発した言語拡張。 JSXはReactとともに普及し、 現在ではマークアップのデファクトスタンダードとなっている。 独自文法とは異なり周辺ツールチェーンの恩恵を受けやすいのも大きなメリット。 Parser Linter / Formatter Transformer etc…

    Slide 16

    Slide 16 text

    2章 Fine-Grained Reactivity

    Slide 17

    Slide 17 text

    SolidJS 3年前、SolidJS(v1)が登場。Reactと同じくJSXを採用しながら、仮想DOMを 使わない宣言的UIを実現。 状態は全てSignalsで管理。Signalsの値が変化すると、それを検知して該当の Signalsが使われたDOMを更新。 これがいわゆるFine-Grained Reactivity。 Fine-grained: きめの細かい

    Slide 18

    Slide 18 text

    Signals Fine-Grained ReactivityのベースにあるのがSignals。Signalsは StreamやObservableのような概念で、単一の値を持ちその値が変化すると通 知できる。 StreamやObservableではなく、Signalsが宣言的UIで重宝されるのはインタ ーフェースと柔軟性のバランスが取れているため。

    Slide 19

    Slide 19 text

    Signals これらは状態を購読する例。 // React Hooks const [count, setCount] = useState(0); useEffect(() => { console.log(count); }, [count]); // Svelte (svelte/store) const count = writable(0); count.subscribe((value) => { console.log(value); }); // Vue.js (@vue/reactivity) const count = ref(0); watchEffect(() => { console.log(count.value); });

    Slide 20

    Slide 20 text

    SolidJSの弱点 SolidJSのコンポーネントは1度しか実行されない。 1度実行でコンポーネントのすべての状態を返す必要がある(イメージ)。 また、それゆえの制約がある。

    Slide 21

    Slide 21 text

    SolidJSの弱点 - 制約1 算出プロパティを使う時は関数にする function App() { const [count, setCount] = createSignal(0); const double = count() * 2; return ( <>

    {count()} * 2 = {double}

    setCount((c) => c + 1)}> +1 > ); }

    Slide 22

    Slide 22 text

    SolidJSの弱点 - 制約2 早期リターンができない function App() { const [count, setCount] = createSignal(0); if (count() === 0) { return setCount(1)}>Start!; } return

    Count is {count()}

    ; }

    Slide 23

    Slide 23 text

    Svelte 5 と Vue Vapor いずれもFine-Grained Reactivityを実現。(Svelte 5は10月リリース、 Vue VaporはWIP) SolidJSとは異なり独自の文法を提供しているため、「SolidJSのような制約を 軽減している」とも言える。 そもそもリターンを書かないから早期リターンもない。 一方で、JSXではない故の問題もある。 最近はRust製の高速なツールチェーンが登場しているが、対応が後回しになりが ち。

    Slide 24

    Slide 24 text

    SolidJS と Svelte 5 と Vue Vapor いずれも開発者が書いたコードがコンパイルされ、関数コンポーネントになる。 この関数コンポーネントは実際のDOMを返す(Svelteは若干異なるが省略)。 defineProps(["name"]);

    Hello {{ name }}!

    // コンパイル後 ( 一部手で調整) const t0 = _template("

    ") function render(_ctx, $props) { const n0 = t0() _renderEffect(() => { _setText(n0, "Hello ", $props.name, "!") }) return n0 }

    Slide 25

    Slide 25 text

    Fine-Grained Reactivity Virtual DOM is pure overhead に対する1つの答えが Fine-Grained Reactivity。 そもそも仮想DOMを使わなければ、仮想DOMのオーバーヘッドはなくなる。 オブジェクトや関数のアロケーションも、関数コンポーネント自体は一度しか呼ばれ ないので問題にならない。 主なプレイヤーはSolidJS、Svelte 5、Vue Vapor。 (Vue Vaporは仮想DOMモードとの併用も可能)

    Slide 26

    Slide 26 text

    閑話休題 Signals Fine-Grained Reactivityの基本はSignals。 このSignal、各フレームワークが独自に実装している。当然互換性はない。 そこで、TC39のプロポーザルとしてSignals標準化の動きがある。(Stage 1) 現状すぐに使えるわけではないが、標準化されれば各Fine-Grained Reactivity系フレームワークの互換性が向上する可能性もある。 (パフォーマン スはそれほど変わらない)

    Slide 27

    Slide 27 text

    2.7章 いまとこれから 仮想DOMが宣言的UIを広めてきたが、仮想DOMを使わない宣言的UIも勢力を 拡大している。 Reactは仮想DOM由来の問題を解決するためにReact Compilerを開発中。 React Compilerは名前の通りReact(のコンポーネント)をコンパイルする。 他のライブラリはSignalを使ったFine-Grained Reactivityがアツい。 Fine-Grained Reactivityでは仮想DOMを使わず、状態に追従する実際の DOM要素を作り出す。

    Slide 28

    Slide 28 text

    これからの宣言的UIに必要な要素 いまを考えると、これからの宣言的UIには次のような要素が求められると考えら れる。 1. パフォーマンス 命令型のコード(=フレームワークなし)に漸近するスピード 2. 開発体験 開発者が違和感のないコードを書けること 外部ツールとの親和性 3. 互換性 既存コードとの親和性

    Slide 29

    Slide 29 text

    宣言的UIと仮想DOMとFine-Grained Reactivity 宣言的UIはWeb開発の標準となった。 React CompilerやFine-Grained Reactivityは、 これからの宣言的UIのキモになるかもしれない。 一方で、仮想DOMが極端に遅いわけではない。 我々は現実的なスピードで動作するWebアプリを作っているし、使えている。 新しい技術を注視しつつ、いまある仮想DOMなどの技術と課題を見つめていけ ばよい。

    Slide 30

    Slide 30 text

    まとめ 宣言的UIはWebのものではなく、広く使われるようになっている 仮想DOMは宣言的UIを広めたが、オーバーヘッドも問題視されている React CompilerはReactの仮想DOMオーバーヘッドを解決する Fine-Grained Reactivityは仮想DOMを使わず、宣言的UIを実現する これからの宣言的UIには高いパフォーマンス・開発体験・互換性が求められる 仮想DOMも死なないので引き続き魂を震わせてOK