React と Vue.js の概要
フロントエンド開発で最も人気のある2つのフレームワーク(ライブラリ)、ReactとVue.jsを比較します。
基本情報
| 項目 | React | Vue.js |
|---|---|---|
| 開発元 | Meta (Facebook) | Evan You + コミュニティ |
| 初回リリース | 2013年 | 2014年 |
| 最新メジャー | React 19 | Vue 3.5+ |
| ライセンス | MIT | MIT |
| GitHub Stars | 230k+ | 210k+ |
| 分類 | UIライブラリ | プログレッシブフレームワーク |
コードの書き方の違い
カウンターコンポーネント
React(関数コンポーネント + Hooks)
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<button onClick={() => setCount(count + 1)}>
増やす
</button>
</div>
);
}
Vue.js(Composition API + SFC)
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<div>
<p>カウント: {{ count }}</p>
<button @click="count++">
増やす
</button>
</div>
</template>
TODOリスト
React
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (!input.trim()) return;
setTodos([...todos, { id: Date.now(), text: input, done: false }]);
setInput('');
};
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
));
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTodo()}
/>
<button onClick={addTodo}>追加</button>
<ul>
{todos.map(todo => (
<li
key={todo.id}
onClick={() => toggleTodo(todo.id)}
style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
>
{todo.text}
</li>
))}
</ul>
</div>
);
}
Vue.js
<script setup>
import { ref } from 'vue';
const todos = ref([]);
const input = ref('');
function addTodo() {
if (!input.value.trim()) return;
todos.value.push({ id: Date.now(), text: input.value, done: false });
input.value = '';
}
function toggleTodo(todo) {
todo.done = !todo.done;
}
</script>
<template>
<div>
<input v-model="input" @keydown.enter="addTodo" />
<button @click="addTodo">追加</button>
<ul>
<li
v-for="todo in todos"
:key="todo.id"
@click="toggleTodo(todo)"
:style="{ textDecoration: todo.done ? 'line-through' : 'none' }"
>
{{ todo.text }}
</li>
</ul>
</div>
</template>
主な違い
| 観点 | React | Vue.js |
|---|---|---|
| テンプレート | JSX(JS内にHTML) | SFC(HTML/JS/CSS分離) |
| 状態管理 | イミュータブル(setStateで更新) | リアクティブ(直接変更可能) |
| イベント | onClick, onChange | @click, v-model |
| ループ | {array.map(item => ...)} | v-for="item in array" |
| 条件分岐 | {condition && <div>...</div>} | v-if="condition" |
| CSS | CSS-in-JS, CSS Modules等 | Scoped CSS(SFC内) |
リアクティビティの仕組み
React: イミュータブルな状態管理
// 状態の更新は必ずsetter関数を通す
const [user, setUser] = useState({ name: '田中', age: 30 });
// NG: 直接変更しても再レンダリングされない
user.name = '佐藤';
// OK: 新しいオブジェクトを作ってセットする
setUser({ ...user, name: '佐藤' });
Vue.js: Proxyベースのリアクティビティ
const user = reactive({ name: '田中', age: 30 });
// OK: 直接変更すると自動で再レンダリングされる
user.name = '佐藤';
// ref の場合は .value でアクセス
const count = ref(0);
count.value++; // テンプレート内では .value 不要
エコシステム比較
ルーティング
| React | Vue.js | |
|---|---|---|
| 公式 | なし(React Router が事実上標準) | Vue Router(公式) |
| 代表的なライブラリ | React Router, TanStack Router | Vue Router |
状態管理
| React | Vue.js | |
|---|---|---|
| 公式 | なし | Pinia(公式推奨) |
| 代表的なライブラリ | Zustand, Jotai, Redux | Pinia |
メタフレームワーク(SSR/SSG)
| React | Vue.js | |
|---|---|---|
| SSR/SSG | Next.js, Remix | Nuxt |
| 静的サイト | Astro, Gatsby | Astro, VitePress |
UIコンポーネントライブラリ
| React | Vue.js | |
|---|---|---|
| 代表的なもの | shadcn/ui, MUI, Ant Design | Vuetify, Element Plus, PrimeVue |
パフォーマンス
バンドルサイズ
| React | Vue.js | |
|---|---|---|
| コアライブラリ | ~45KB (gzipped) | ~34KB (gzipped) |
| 最小構成 | react + react-dom | vue |
ランタイムパフォーマンス
React 19のコンパイラとVue 3のリアクティビティシステムにより、どちらも十分に高速です。一般的なWebアプリケーションでは体感差はほぼありません。
最適化のアプローチ
React: React Compiler(自動メモ化)、React Server Components
// React 19: Compilerが自動で最適化するため、手動メモ化が不要に
function UserList({ users }) {
// useMemo, useCallback が多くの場合不要
return users.map(user => <UserCard key={user.id} user={user} />);
}
Vue.js: コンパイラによる静的解析と最適化
<!-- Vue 3: テンプレートコンパイラが自動で最適化 -->
<template>
<div>
<h1>タイトル</h1> <!-- 静的ノード: 更新チェックをスキップ -->
<p>{{ dynamicText }}</p> <!-- 動的ノード: これだけ更新 -->
</div>
</template>
学習コスト
React の学習曲線
- JSX の理解
- Hooks(useState, useEffect, useRef…)
- 状態管理のイミュータブルパターン
- レンダリングの最適化(memo, useMemo, useCallback)
- 外部ライブラリの選定(ルーティング、状態管理、フォーム等)
Vue.js の学習曲線
- テンプレート構文(v-if, v-for, v-model…)
- SFC(Single File Component)の構造
- Composition API(ref, reactive, computed, watch)
- 公式ライブラリ(Vue Router, Pinia)
Vue.jsは公式が提供する範囲が広いため、「何を使えばいいか」の判断が少なくて済みます。
TypeScript 対応
React + TypeScript
interface UserProps {
name: string;
age: number;
onUpdate: (name: string) => void;
}
function UserCard({ name, age, onUpdate }: UserProps) {
return (
<div>
<h2>{name} ({age}歳)</h2>
<button onClick={() => onUpdate('新しい名前')}>更新</button>
</div>
);
}
Vue.js + TypeScript
<script setup lang="ts">
interface Props {
name: string;
age: number;
}
const props = defineProps<Props>();
const emit = defineEmits<{
update: [name: string];
}>();
</script>
<template>
<div>
<h2>{{ name }} ({{ age }}歳)</h2>
<button @click="emit('update', '新しい名前')">更新</button>
</div>
</template>
選定ガイド
React を選ぶべきケース
- 大規模チーム: エンジニアの採用がしやすい(求人数が多い)
- React Native: モバイルアプリも同時に開発する場合
- 柔軟性重視: ライブラリを自由に組み合わせたい
- Next.js: SSR/SSGにNext.jsを使いたい
Vue.js を選ぶべきケース
- 少人数チーム: 公式エコシステムが充実しており判断が少ない
- 学習コスト重視: HTMLに近い構文で入りやすい
- Scoped CSS: CSSのスコープ管理をシンプルにしたい
- 段階的な導入: 既存プロジェクトの一部に組み込みやすい
まとめ
| 判断軸 | React | Vue.js |
|---|---|---|
| 求人数 | ◎ | ○ |
| 学習しやすさ | ○ | ◎ |
| 公式エコシステム | △(選定が必要) | ◎ |
| 柔軟性 | ◎ | ○ |
| TypeScript | ◎ | ◎ |
| パフォーマンス | ◎ | ◎ |
2026年時点では、どちらを選んでも間違いはありません。チームのスキルセット、プロジェクトの要件、採用市場を考慮して決定しましょう。
コンポーネントのpropsやAPIレスポンスの構造を確認するには、AssistyのJSONフォーマッターが便利です。