Slide 1

Slide 1 text

フロントエンドUIフレームワークの これまでとこれから ssssota #fec_fukuoka

Slide 2

Slide 2 text

Hi there 👋 TOMIKAWA Sotaro / ssssota 仕事はReact、趣味はSvelte/Preact フロントエンドフレームワークぜんぶすき ⼤学の2年間だけ福岡に住んでいました

Slide 3

Slide 3 text

フロントエンドUIフレームワーク

Slide 4

Slide 4 text

React Svelte フロントエンドUIフレームワーク SolidJS Preact Vue.js Angular

Slide 5

Slide 5 text

フロントエンドUIフレームワーク → 今回はWebを第⼀のプラットフォームとする宣⾔的UIを記述するための ライブラリ・フレームワークを指す

Slide 6

Slide 6 text

の、これまでとこれから

Slide 7

Slide 7 text

歴史を順に追っていく

Slide 8

Slide 8 text

HTML(1990年代前半〜) HTMLは、広義の宣⾔的UIである。 ⽂書構造をマークアップするもの。

Slide 9

Slide 9 text

動的なWeb(1995年ごろ〜) JavaScriptの登場、HTMLを操作できるように。 Java Applet, Flash, Silverlightでリッチな表現も登場。

Slide 10

Slide 10 text

Ajax(2005年ごろ〜) Asynchronous JavaScript and XML. ⾮同期で情報を取得して、画⾯を更新するように。

Slide 11

Slide 11 text

jQuery(2006年ごろ〜) jQueryでDOM操作やAjaxが容易に... 操作と更新が増え続け... コードは複雑に...

Slide 12

Slide 12 text

命令的UI

Slide 13

Slide 13 text

// 命令的UI let count = 0; const result = document.getElementById('result'); const doubleResult = document.getElementById('double-result'); const button = document.getElementById('increment'); button.addEventListener('click', () => { count++; result.textContent = count; doubleResult.textContent = count * 2; });

Slide 14

Slide 14 text

Model - View の分離 Backbone.js(2010年ごろ〜)など、 Model - Viewを分離するアプローチもあったが 描画は開発者が管理する必要があり苦しい。

Slide 15

Slide 15 text

宣⾔的UI

Slide 16

Slide 16 text

Knockout

Slide 17

Slide 17 text

Knockout(2012年ごろ〜) テンプレートとObservablesを使ったライブラリ。 `data-bind` 属性をHTMLに⼊れて表現する。

Slide 18

Slide 18 text

+1
const count = ko.observable(0); function increment() { count(count() + 1); } ko.applyBindings({ count, increment });

Slide 19

Slide 19 text

現代に⾄る宣⾔的UIの祖? HTMLとは異なり、 JavaScriptから値を取得し、表⽰ 値の変化に応じて、使⽤箇所を更新する

Slide 20

Slide 20 text

AngularJS, React, Vue.js, ...

Slide 21

Slide 21 text

React → 仮想DOM

Slide 22

Slide 22 text

仮想DOM React(2013年ごろ〜) 状態が変化したら、仮想のDOMツリーを構築、 現在の状態と⽐較することで変化した箇所を特定、更新

Slide 23

Slide 23 text

仮想のDOMツリー

Slide 24

Slide 24 text

再レンダー(この時点で画⾯は変わらない)

Slide 25

Slide 25 text

⽐較(reconciler)

Slide 26

Slide 26 text

実DOM適⽤(commit)

Slide 27

Slide 27 text

オブジェクト指向とコンポーネント

Slide 28

Slide 28 text

class でコンポーネントを定義する コンポーネントの状態、振る舞い、表⽰をまとめて定義する 形式が主流に。 状態はプロパティ、振る舞いはメソッドなど クラスインスタンスをUIコンポーネントとするのは ⼀定理にかなっていた。

Slide 29

Slide 29 text

class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } increment() { this.setState(state => ({ count: state.count + 1 })); } render() { return

{this.state.count}

+1
; } }

Slide 30

Slide 30 text

Vue.js Vue.js 0.X、1.0はKnockout的に使⽤箇所を追跡する。 Vue.js 2.Xは仮想DOMを使うように。

Slide 31

Slide 31 text

{{ count }}

+1
export default { data() { return { count: 0 }; }, methods: { increment() { this.count++; } } };

Slide 32

Slide 32 text

ロジックの分離 クラスやオブジェクトではstateやライフサイクルに紐づくロ ジックの再利⽤が困難だった。

Slide 33

Slide 33 text

React Hooks

Slide 34

Slide 34 text

関数コンポーネントへ `useState` を使って状態を表現できるようになり、 ライフサイクルフック等外部接続は `useEffect` になった。 Hook単体でロジックを持ち、テスト可能、共有可能に。

Slide 35

Slide 35 text

function useCounter() { const [count, setCount] = useState(0); const increment = () => setCount(c => c + 1); return { count, increment }; } function Counter() { const { count, increment } = useCounter(); return

{count}

+1
; }

Slide 36

Slide 36 text

Vue.js Composition API Reactに追従するようにVue 3 (Composition API)が公開 ロジックとデータを集約して関数を定義できる形式。 旧来のOptions APIに親しんでいたユーザーからの反発は ありつつも、現在では受け⼊れられている。

Slide 37

Slide 37 text

{{ count }}

+1
<script> export default { export default { data() { setup() { return { count: 0 }; const count = ref(0); }, const increment = () => { methods: { count.value++; increment() { }; this.count++; return { count, increment }; } } } }; };

Slide 38

Slide 38 text

Signals の普及

Slide 39

Slide 39 text

SolidJSの登場 Signalsをベースに、JSXで記述できる 細粒度リアクティビティなフレームワーク。 「Reactのような関数コンポーネントで⾼パフォーマンス」 といった⾔説とともに登場。

Slide 40

Slide 40 text

Signals 値が格納された箱。 使われている箇所(=依存)が追跡できる。 依存を有向グラフで管理。

Slide 41

Slide 41 text

Fine-grained Reactivity ⽇本語で細粒度リアクティビティ。 Signalsを⽤いることで、利⽤箇所を追跡可能に。 Signalの値が変化した際に、利⽤箇所のみを更新する。

Slide 42

Slide 42 text

export function Counter() { const [count, setCount] = createSignal(0); const increment = () => setCount(c => c + 1); return

{count()}

+1
; }

Slide 43

Slide 43 text

signalを元に実DOMが作られる

Slide 44

Slide 44 text

signalが変化する

Slide 45

Slide 45 text

signalを元に直接書き換える

Slide 46

Slide 46 text

Svelte 5, Vue Vapor

Slide 47

Slide 47 text

細粒度リアクティビティへの移⾏ 独⾃のコンパイル時最適化を⾏っていたSvelteは Svelte 5でSignalsベースの細粒度リアクティビティへ。 Vue.jsもVapor modeを開発。

Slide 48

Slide 48 text

let count = $state(0); function increment() { count = count + 1; }

{count}

+1

Slide 49

Slide 49 text

似たような話を 🤔

Slide 50

Slide 50 text

Knockout was right all along. Knockoutは最初から正しかった。 Svelte 5のRunesを紹介する記事の⼀節。 細粒度リアクティビティはSolidJSが原点ではなく、 Knockoutが原点に近い。 Vue Vaporも仮想DOMがなかったVue 1な仕組みに近い。

Slide 51

Slide 51 text

なぜ現代に帰ってきたのか そもそも仮想DOMが流⾏ったのは、テンプレートではなく JavaScriptとして扱いやすい形で、SSRや他プラットフォー ムへの活⽤などを⾒据えていたため。 そこから細粒度リアクティビティに帰ってきたのは、 JavaScriptのコンパイル技術や最適化が進歩し、 Knockoutの課題感を現代的な基準でクリアできたため。

Slide 52

Slide 52 text

ここまで歴史

Slide 53

Slide 53 text

UIフレームワークをいろんな⾯から⾒る

Slide 54

Slide 54 text

UIフレームワークの記述⽅法

Slide 55

Slide 55 text

JSX (TSX) いまのフロントエンド開発者で知らぬものはいない。 TypeScriptやそれ以外のツールチェインも標準サポートする 数少ないECMAScript拡張構⽂。

Hello

は、 jsx("p", {children: "Hello"}) などと解釈できる。

Slide 56

Slide 56 text

テンプレート (SFC) HTML⾵DSL。 `` や `<style>` タグを利⽤し、装飾も可能。 変換を前提とするので、JavaScriptで表現しづらいことも テンプレートは表現しやすい。 (双⽅向バインディングなど)

Slide 57

Slide 57 text

JSX vs. テンプレート JSX→独⾃の記法を覚える必要が少ない。⽐較的ただのJS 各ツールチェインのサポートを受けやすい。 テンプレート→ UI特化の構⽂で簡潔に記述できる。 細粒度リアクティビティと相性がいい。 ツールチェインのサポートは受けづらい。

Slide 58

Slide 58 text

細粒度リアクティビティ と JSX

Slide 59

Slide 59 text

関数コンポーネントは⼀度だけ実⾏する 細粒度リアクティビティなフレームワークでは、 ⼀般的に関数コンポーネントは初回しか実⾏されない。 ⼀度の実⾏で、コンポーネントの全貌を伝える。 状態変化毎に実⾏されるReactなどに慣れていると違和感。

Slide 60

Slide 60 text

SolidJS JSXを記述し細粒度リアクティビティを実現する。 JSXをコンパイルすることで最適化が⾏われる。 コンポーネント内で早期リターンができないなど、 記法への制約が強い。 分岐や反復のために、``や``がある。

Slide 61

Slide 61 text

export function Counter() { const [count, setCount] = createSignal(0); const increment = () => setCount(c => c + 1); const reset = () => setCount(0); return 0} fallback={Start} >

{count()}

+1 Reset ; }

Slide 62

Slide 62 text

Preact @preact/signals というパッケージを使ってSignalsが 導⼊できる。これにも``や``がある。 Preact⾃体は軽量な仮想DOMベースのライブラリなので、 早期リターンを使うと、従来の差分検出で更新される。

Slide 63

Slide 63 text

Vue JSX Vapor Vue.jsには細粒度リアクティビティを⽤いるVapor modeが ある。JSXも、それらを合わせたVue JSX Vaporもある。 例に漏れず早期リターンができない。 `v-if`や`v-for`といったディレクティブがある。

Slide 64

Slide 64 text

function Counter() { const count = ref(0); const increment = () => { count.value += 1; }; const reset = () => { count.value = 0; }; } return ( <> 0}>

{count.value}

+1 Reset Start );

Slide 65

Slide 65 text

テンプレートと細粒度リアクティビティ テンプレートの場合は早期リターンという概念⾃体が 存在しないため、概念上の整合性が取りやすい。 Vue.jsやSvelteは独⾃の記法を覚える必要はあれど、その実 細粒度リアクティビティと相性は悪くない。

Slide 66

Slide 66 text

let count = $state(0); const increment = () => { count += 1; }; const reset = () => { count = 0; }; {#if count > 0}

{count}

+1 Reset {:else} Start {/if}

Slide 67

Slide 67 text

テンプレートとTypeScript

Slide 68

Slide 68 text

TypeScriptのサポートを受けるために TypeScriptはJSXしかサポートしていないので、 Vue.js、Svelteなどは独⾃のサポートが必要。 LSPをラップしたCLI、IDE拡張をそれぞれ提供している。 ● vue-tsc ● svelte-check

Slide 69

Slide 69 text

TypeScriptの進化

Slide 70

Slide 70 text

TypeScript Content mappers tsgo化に際しプラグイン機能が利⽤できなくなった。 それを置き換える形でContent mappers APIが整理中。 Vue.jsやSvelteなどの型サポートがより容易かつ⾼速に。 IPC(プロセス間通信)を⽤いる。

Slide 71

Slide 71 text

第3のテンプレート

Slide 72

Slide 72 text

TSRX React, Svelteなどに携わってきたDominic Gannaway⽒ が主導する「JSXの精神的後継」。 JSXのようにフレームワーク中⽴ながら``を使える、 分岐や反復を明⽰的に。

Slide 73

Slide 73 text

export function Counter() @{ const [count, setCount] = createSignal(0); const increment = () => setCount(c => c + 1); const reset = () => setCount(0); @if (count() > 0) { <>

{count()}

+1 Reset } @else { Start } }

Slide 74

Slide 74 text

UIフレームワークのパフォーマンス

Slide 75

Slide 75 text

実⾏速度 細粒度リアクティビティがわずかに優位。 仮想DOMなアプローチではツリーの構築・差分検出が ⾼コストになりがち。

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

Reactの記述で速くできるか

Slide 79

Slide 79 text

React Compiler Reactのメモ化を⾃動で⾏う。 無駄な計算をスキップできる可能性が⾼まる。 React FirというプロジェクトもReact Confで共有されたが 情報は特に出ていない。

Slide 80

Slide 80 text

Octane Dominic Gannaway⽒のプロジェクト。 JSX React⾵の記述で⾼いパフォーマンスを 仮想DOMも、Signalsも無しで実現する。 状態の変化で関数コンポーネントは再実⾏される。 JSXはDOM操作のコードに変換される。

Slide 81

Slide 81 text

バンドルサイズ 細粒度リアクティビティが⼩さい傾向にあるものの、 仮想DOMだからといって⼤きいわけではない。 Preactは⼩さいし、Reactは⼤きい。

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

Reactはデカい

Slide 84

Slide 84 text

⼩型のReact ⾔わずと知れたPreact、Reactに追従できていないため、 互換とは⾔いづらい。 @tanstack/redact、ReactのAPI互換性を重視しつつ、 Transitionなどを削ることでバンドルサイズ削減を果たす。

Slide 85

Slide 85 text

Scheduler, Lane, Fiber, ... Reactでは、状態の更新の優先度がLaneで管理され、 タスクはschedulerに委託し、、 Fiberツリーでこれらを持ちまわり、、、 圧倒的なまでの抽象化。何を抽象化しているか?

Slide 86

Slide 86 text

Suspense / Transition

Slide 87

Slide 87 text

表と裏 タスクの始まりをユーザーに⾒せつつ、タスクの完了を待つ ● useTransitionのisPendingで変更の待機を⾒せる ● Suspenseで取得を待つ ユーザーの体験を損ねないための機能 →⾮同期のベースになっている

Slide 88

Slide 88 text

Async React

Slide 89

Slide 89 text

⾮同期を前提に Async ReactはReactを⾮同期に考え直すもの。 ライブラリやフレームワークも含めた⽅向性を⽰すもの。 現状のReact公式ドキュメントLearnには TransitionやSuspenseについての⾔及がない。 ドキュメントの更新もAsync Reactのスコープ。

Slide 90

Slide 90 text

Async **

Slide 91

Slide 91 text

Asynchronous Svelte Svelteの⾮同期処理をより強⼒にする提案。6で安定化予定 コンポーネントトップレベルでawaitを可能に。 ReactはユーザーがTransitionを強く意識する必要があった ⼀⽅、Svelteは⾮同期を⾃動Transitionするような仕組みに

Slide 92

Slide 92 text

Async Solid (Solid 2.0) v1に存在した⾮同期向けAPIは削除、 AsyncをSignalsのプリミティブとして扱う。 awaitをコンポーネント内で書かずとも、 同期処理と同様に(透過的に)⾮同期が扱える。 こちらも⾮同期を強く意識させない構造に。

Slide 93

Slide 93 text

function Profile(props) { const user = createMemo(() => fetchUser(props.id)); } return ( }>

{user().name}

);

Slide 94

Slide 94 text

Async Vue ? ``が実験的に存在しtop-level awaitなどが可能 lazy loadもSuspenseを活⽤。 reactivity coreへの⾮同期統合はされていない。 提案⾃体は存在する。

Slide 95

Slide 95 text

⾮同期は誰が為に

Slide 96

Slide 96 text

⾮同期を扱うコンポーネント ⾮同期の仕組みがフレームワークに統合された形で 設計されたものはほぼ存在しなかった。 「データフェッチをコンポーネントで」+「SSR」 この2つを統合していく上で必要性が明らかになってきた。 所謂コロケーション。

Slide 97

Slide 97 text

データフェッチを扱うために ● UIフレームワークからのアプローチ ○ ○ React - React Server Components / Server Functions Solid 2.0 - Server Functions ○ ○ ○ TanStack Start - Server Functions Nuxt - Server Components SvelteKit - Remote Functions ○ GraphQL Fragment Colocation ● メタフレームワークからのアプローチ ● フレームワーク外からのアプローチ

Slide 98

Slide 98 text

React Server Components Reactがライブラリの域を超えた。 Server Componentsはクライアントで実⾏されない。 データフェッチ以外もまとめて解決する。

Slide 99

Slide 99 text

Solid 2.0 Server Functions 所謂RPC。 TanStack Startなど、メタフレームワーク側で提供されるよ うな機能をUIフレームワークの⼀部として提供。 SolidStartというメタフレームワークは姿を消し統合。

Slide 100

Slide 100 text

⾮同期システムの統合

Slide 101

Slide 101 text

→RPCの統合

Slide 102

Slide 102 text

→メタフレームワークの統合

Slide 103

Slide 103 text

UIフレームワークの現実

Slide 104

Slide 104 text

Reactの圧倒的な使⽤率 2025.stateofjs.com (Usage) tanstack.com/stats (npm downloads)

Slide 105

Slide 105 text

Reactの圧倒的な使⽤率 2025.stateofjs.com (Usage) tanstack.com/stats (npm downloads)

Slide 106

Slide 106 text

Reactの圧倒的な使⽤率 2025.stateofjs.com (Usage) tanstack.com/stats (npm downloads)

Slide 107

Slide 107 text

Reactの圧倒的な周辺ライブラリ Vue.jsやSvelteもライブラリは健闘している。 TanStack等 複数フレームワークに対応するものも増えた。 リッチテキストエディタ、アクセシビリティ、表、D&Dなど ニッチな領域でも有⼒な選択肢がいくつかあるのがReact。

Slide 108

Slide 108 text

React Foundation Meta、Vercelなど複数の企業が組んでReactを⽀える体制 が整っている。 ReactはMetaだけのものではなくなった。

Slide 109

Slide 109 text

まとめ

Slide 110

Slide 110 text

これからのUIフレームワーク 「宣⾔」の範囲、「フレームワーク」の範囲、が拡⼤する。 ReactのTransitionは並⾏性の宣⾔と⾒ることができるし、 Signalsは状態の宣⾔。 UIフレームワークとメタフレームワークの距離が近くなり、 より密接になっていく。

Slide 111

Slide 111 text

React その確かな技術⼒、利⽤者・ライブラリの多さ、React Foundationなど盤⽯のUIフレームワーク。 Suspense/Transitionは開発者が明⽰的に取り⼊れていく 必要があり、取り⼊れられなければ恩恵が享受できない。 次々と⾰新的な技術を投⼊するReactについていけるか。

Slide 112

Slide 112 text

React以外 細粒度リアクティビティがトレンド。 内部に細かい差はあれど、構⽂の違いだけが⽬⽴つ状態。 SolidJSやSvelteが並⾏性や⾮同期を扱う仕組みを取り⼊れ リードしている。 パフォーマンスを追求するケースではこちらに分があるか。

Slide 113

Slide 113 text

おわり UIフレームワークという領域は今もなお進化を続けて、 フロントエンドという障害だらけの道を切り拓いている。 答えはないので、進化を追うのみ。 (正直枯れて欲しい気持ちも)Oo。