📑
React, Vue.js, Svelte比較してみよう
こんにちは。FEチームのMapleです。私たちのチームは、現在のシステムアーキテクチャを見直し、Reactを用いた新しいアーキテクチャへの移行を検討しています。直近ではもう少し視野を広げてVueからの移行にはSvelteのほうが向いているのではないかと考えているので振り返ってすべて比較してみます!
比較内容
以下の機能を持つシンプルなアプリケーションを実装します。
- カウンター機能:ボタンをクリックするとカウントが1増える。
- APIデータ取得:外部APIからデータを取得し、リスト表示する。
- フォームハンドリング:入力フォームからデータを取得し、リストに追加する。
- コンポーネント間通信:親子コンポーネント間でデータをやり取りする。
1. カウンター機能の実装
React
// Counter.jsx
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Vue.js
<!-- Counter.vue -->
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
export default {
data() {
return { count: 0 };
},
methods: {
increment() {
this.count++;
}
}
};
</script>
Svelte
<!-- Counter.svelte -->
<script>
let count = 0;
</script>
<div>
<p>Count: {count}</p>
<button on:click={() => count++}>Increment</button>
</div>
2. APIデータ取得の実装
React
// DataFetcher.jsx
import { useState, useEffect } from "react";
function DataFetcher() {
const [items, setItems] = useState([]);
useEffect(() => {
fetch("https://api.example.com/items")
.then(response => response.json())
.then(data => setItems(data));
}, []);
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
export default DataFetcher;
Vue.js
<!-- DataFetcher.vue -->
<template>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script>
export default {
data() {
return { items: [] };
},
created() {
fetch("https://api.example.com/items")
.then(response => response.json())
.then(data => (this.items = data));
}
};
</script>
Svelte
<!-- DataFetcher.svelte -->
<script>
let items = [];
onMount(async () => {
const response = await fetch("https://api.example.com/items");
items = await response.json();
});
</script>
<ul>
{#each items as item}
<li>{item.name}</li>
{/each}
</ul>
3. フォームハンドリングの実装
React
// FormHandler.jsx
import { useState } from "react";
function FormHandler() {
const [inputValue, setInputValue] = useState("");
const [list, setList] = useState([]);
const handleSubmit = e => {
e.preventDefault();
setList([...list, inputValue]);
setInputValue("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
value={inputValue}
onChange={e => setInputValue(e.target.value)}
type="text"
/>
<button type="submit">Add</button>
</form>
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default FormHandler;
Vue.js
<!-- FormHandler.vue -->
<template>
<div>
<form @submit.prevent="handleSubmit">
<input v-model="inputValue" type="text" />
<button type="submit">Add</button>
</form>
<ul>
<li v-for="(item, index) in list" :key="index">{{ item }}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
inputValue: "",
list: []
};
},
methods: {
handleSubmit() {
this.list.push(this.inputValue);
this.inputValue = "";
}
}
};
</script>
Svelte
<!-- FormHandler.svelte -->
<script>
let inputValue = "";
let list = [];
const handleSubmit = (e) => {
e.preventDefault();
list = [...list, inputValue];
inputValue = "";
};
</script>
<div>
<form on:submit|preventDefault={handleSubmit}>
<input bind:value={inputValue} type="text" />
<button type="submit">Add</button>
</form>
<ul>
{#each list as item}
<li>{item}</li>
{/each}
</ul>
</div>
4. コンポーネント間通信の実装(親子コンポーネント)
React
// Parent.jsx
import Child from "./Child";
function Parent() {
const handleChildData = (data) => {
console.log("Received from child:", data);
};
return <Child onSendData={handleChildData} />;
}
export default Parent;
// Child.jsx
function Child({ onSendData }) {
const data = "Hello from Child";
return (
<button onClick={() => onSendData(data)}>Send Data to Parent</button>
);
}
export default Child;
Vue.js
<!-- Parent.vue -->
<template>
<Child @send-data="handleChildData" />
</template>
<script>
import Child from "./Child.vue";
export default {
components: { Child },
methods: {
handleChildData(data) {
console.log("Received from child:", data);
}
}
};
</script>
<!-- Child.vue -->
<template>
<button @click="sendData">Send Data to Parent</button>
</template>
<script>
export default {
methods: {
sendData() {
this.$emit("send-data", "Hello from Child");
}
}
};
</script>
Svelte
<!-- Parent.svelte -->
<script>
import Child from "./Child.svelte";
function handleChildData(event) {
console.log("Received from child:", event.detail);
}
</script>
<Child on:sendData={handleChildData} />
<!-- Child.svelte -->
<script>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
function sendData() {
dispatch("sendData", "Hello from Child");
}
</script>
<button on:click={sendData}>Send Data to Parent</button>
コードの比較
特徴 | React | Vue.js | Svelte |
---|---|---|---|
状態管理 |
useState , useEffect フック |
data , computed , methods
|
単純な変数とリアクティブ宣言 |
イベント処理 | JSXでイベントを設定 | ディレクティブ(@click )を使用 |
on:click で簡潔に記述 |
フォームバインディング | 明示的にvalue とonChange を設定 |
v-model で双方向バインディング |
bind:value で双方向バインディング |
コンポーネント間通信 | Propsとコールバック関数を使用 | Propsと$emit でイベントを伝播 |
PropsとcreateEventDispatcher を使用 |
ライフサイクルフック |
useEffect フックで管理 |
created , mounted などを使用 |
onMount , beforeUpdate などを使用 |
テンプレート構文 | JSX(JavaScript拡張) | 独自のテンプレート構文 | HTMLに近いテンプレート構文 |
学習コスト | 高い | 低め | 低め |
ランタイム依存 | 必要 | 必要 | 不要 |
パフォーマンス | 仮想DOMを使用 | 仮想DOMを使用 | 直接DOM操作で高速 |
詳細な比較ポイント
1. 状態管理
-
React:
useState
とuseEffect
フックを組み合わせて状態と副作用を管理。 -
Vue.js:
data
で状態を宣言し、computed
やmethods
でロジックを定義。 - Svelte:単純に変数を宣言し、リアクティブな更新は自動で処理。
2. イベントハンドリング
- React:JSX内でイベントを直接設定し、関数を渡す。
-
Vue.js:
@click
などのディレクティブを使用して直感的に設定。 -
Svelte:
on:event
の形式でシンプルに記述。
3. フォームバインディング
-
React:
value
とonChange
を手動で設定。 -
Vue.js:
v-model
で双方向バインディングが簡単に実現。 -
Svelte:
bind:value
で双方向バインディングが可能。
4. コンポーネント間通信
- React:親から子へはProps、子から親へはコールバック関数を渡す。
-
Vue.js:
props
と$emit
を使ってデータの受け渡し。 -
Svelte:
props
とcreateEventDispatcher
を使用してイベントを発行。
5. ライフサイクルフック
-
React:
useEffect
でライフサイクルに相当する処理を行う。 -
Vue.js:
created
やmounted
などの明示的なフックを使用。 -
Svelte:
onMount
,beforeUpdate
,afterUpdate
などの関数を使用。
結論
- 個人的にはSvelteがコードが完結かつVueからの移行なら良いのではないかと考え始めています!
- 最初はTypeScriptとSvelteは相性悪いと持っていたのですが、そうでもないみたいですね。
参考資料
株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion