ReactとVueは、どちらもコンポーネントからユーザーインターフェースを作るJavaScriptの道具で(Reactは自らをライブラリと、Vueはプログレッシブフレームワークと呼んでいます)、ほとんどの考え方を共有しています。propsは下へ、イベントは上へ、リアクティブなstate、仮想DOMです。実際の違いは、ReactがマークアップをJSXで書き、stateはセッターで置き換えることを求めるのに対し、Vueは単一ファイルコンポーネントの中のHTMLテンプレートでマークアップを書き、リアクティブな値を直接変えられることです。次はReactのカウンターです。
そして同じカウンターを、Vueの単一ファイルコンポーネント(Counter.vue)にしたものです。
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<button @click="count++">Clicked {{ count }} times</button>
</template>
useState(0) は ref(0) に、onClick は @click に、{count} は {{ count }} になります。目に見える違いは count++ です。Vueでは値をその場で変えられ、リアクティビティの仕組みがそれに気づきます。Reactは新しい値を渡されたときにだけ再レンダリングするので、setCount が必要です。
JSXと単一ファイルコンポーネント
Reactのコンポーネントは、JSXを返すJavaScriptの関数です。条件は && か三項演算子、リストは .map() で、スタイルは自分で選んだもの(CSSファイル、CSS Modules、ユーティリティフレームワーク)から来ます。
Vueのコンポーネントは通常、3つのブロックを持つ .vue ファイルです。ロジックのための <script setup>、マークアップのための <template>、そしてCSSがそのコンポーネントにだけ適用される省略可能な <style scoped> です。テンプレートはディレクティブを使います:v-if、v-for、入力の双方向バインディングのための v-model、値をバインドする :prop、イベントを監視する @event。テンプレートはHTMLを知っている人なら誰でも読みやすいマークアップを保ち、JSXはマークアップの中でJavaScriptのすべてを使わせてくれます。VueもJSXを使えますが、Reactはテンプレートを使えません。それでもどちらのコードも、ほとんどはそれぞれの標準に従っています。
比べる2つ目の機能として、フィルターしたリストを見てみましょう。
xyz と入力して空のときのメッセージを確認し、それから削除してください。Vue版:
<script setup>
import { ref, computed } from 'vue';
const fruits = ['Apple', 'Banana', 'Cherry', 'Grape', 'Mango', 'Peach'];
const query = ref('');
const visible = computed(() =>
fruits.filter((f) => f.toLowerCase().includes(query.value.toLowerCase()))
);
</script>
<template>
<input v-model="query" placeholder="Filter fruit" />
<ul>
<li v-for="fruit in visible" :key="fruit">{{ fruit }}</li>
<li v-if="visible.length === 0">No match</li>
</ul>
</template>
v-model が value と onChange の組に代わり、computed が普通の変数に代わります。Reactでは、コンポーネント関数がもう一度実行されるので、visible はレンダリングのたびに計算し直されます。Vueでは、computed がそれをキャッシュし、query が変わったときだけ計算し直します。どちらもリストの項目を区別するために key を使います。
リアクティビティの仕組み
これが両者の一番深い違いです。
React:イミュータブルなstateと再レンダリング。stateはスナップショットです。新しい値でセッターを呼ぶと、Reactはコンポーネント関数をもう一度実行し、新しいJSXを古いものと比べて、DOMを更新します。Reactは値を Object.is で比べるので、オブジェクトや配列をその場で変えて同じものを渡し直すと、まったく変化がないように見えます。
「push() in place」をクリックしても、配列が同じオブジェクトなので画面は何も変わりません。次に「New array」をクリックすると、pushed も表示されます。前の直接の変更は本当に起きていて、Reactに見る理由がなかっただけなのです。このパターンはuseStateのページで詳しく扱っています。
Vue:変更可能なリアクティブな値。ref や reactive のオブジェクトは、誰がそれを読むかを記録します(ref は .value のゲッターを通じて、reactive のオブジェクトはプロキシを通じて)。それに書き込むと、Vueはどのcomputedな値、ウォッチャー、テンプレートの部分がそれに依存しているかを正確に知り、それらだけを更新します。Vueでは items.value.push('pushed') で画面が更新されます。書く手間は減りますが、値が知らないうちに変わらないという保証は手放すことになります。
典型的なアプリで問題になるほどどちらかが速いということはありません。Reactは変更ごとにより多くのコンポーネントのコードを実行することがあり、作業を省くのにメモ化(手書き、またはReact Compiler)に頼ります。Vueは実行時に依存関係を追跡するので、デフォルトでその作業を省きます。
Composition APIとフック
Vue 3のComposition APIとReactのフックは同じ問題を解決します。関連するstateとロジックをまとめ、コンポーネントをまたいで再利用することです。Vueは再利用できる関数をコンポーザブル(useMouse())と呼び、Reactはカスタムフック(useMouse())と呼びます。ルールが違うのは、コードが実行されるタイミングのためです。
- いつ実行されるか。Vueの
setupはコンポーネントが作られたときに1回だけ実行されます。Reactのコンポーネント関数はレンダリングのたびに実行され、その中のフックも一緒に実行されます。 - 順番のルール。Reactは呼び出しの順番でフックを対応させるので、条件分岐やループの中では呼べません。Vueでは
setupが1回しか実行されないので、refやcomputedにそうしたルールはありません。 - 依存値。Reactのエフェクトやメモは、自分で管理する配列に依存値を並べます。Vueの
watchEffectとcomputedは、読むものを自動で追跡します。 - 古い値。Reactのコールバックは、前のレンダリングの古いstateの値を捕まえてしまうことがあります(よくある
setIntervalのバグ)。Vueのコールバックは呼ばれた時点でcount.valueを読むので、常に現在の値を見ます。
Reactの仕組みでは、すべてのレンダリングがpropsとstateの純粋関数になり、テストも考えるのも簡単です。Vueの仕組みは、依存配列にまつわる種類のバグをなくします。一方を知っている開発者は、たいていもう一方もすぐに身につけます。
エコシステム
| React | Vue | |
|---|---|---|
| マークアップ | JSX | .vue ファイルの中のHTMLテンプレート(JSXも可) |
| stateの更新 | セッターで置き換え、再レンダリングする | リアクティブな値を変え、依存するものが更新される |
| ロジックの再利用 | カスタムフック | コンポーザブル |
| 導出される値 | レンダリング中に計算、useMemo | computed(キャッシュされる) |
| 入力の双方向バインディング | value と onChange | v-model |
| スコープ付きCSS | 組み込みではない(CSS Modules、ライブラリ) | <style scoped> が組み込み |
| ルーティング | React Router、TanStack Router、フレームワーク | Vue Router(公式) |
| グローバルなstate | コンテキスト、Zustand、Redux Toolkit | Pinia(公式) |
| フルスタックのフレームワーク | Next.js、フレームワークモードのReact Router | Nuxt |
| モバイルアプリ | React Native | 公式の選択肢はない(Capacitor、NativeScript-Vue) |
| ビルドツール | Viteかフレームワーク | Vite |
| 保守しているのは | Metaとコミュニティ | 独立したコアチームとコミュニティ |
この表から見えるパターンは、Vueのコアチームがルーター、ストア、開発ツールをまとめて保守しているので、Vueのプロジェクトにはそれぞれに明らかな標準があるということです。Reactはそれらをコミュニティに任せているので、選択肢が多い代わりに、決めることも多くなります。どちらの新しいプロジェクトでもよく使われるビルドツールのViteは、Vueの作者が始めたものです。
学習コスト
Vueのほうが穏やかな出だしになることが多いです。テンプレートはすでに知っているHTMLのように読め、v-model が制御された入力の決まり文句をなくし、リアクティブな値は変えると画面を更新します。Reactは最初からJSXとイミュータブルな更新の習慣を求めるので、ほとんどの人が最初に必要とするのはJSXのページです。
基本を過ぎれば、差は縮まります。Vueには独自の概念(refとreactiveなオブジェクト、スクリプトでは .value が要るがテンプレートでは要らないこと、ウォッチャー)が加わり、Reactにはエフェクト、メモ化、フックのルールが加わります。決め手にするなら、構文より住んでいる地域の求人のほうが役立ちます。
どちらを選ぶべきか
次のようなときはReactを選んでください。
- コミュニティのライブラリやコンポーネントの幅広い選択肢がほしいとき。またはReact Nativeでネイティブのモバイルアプリを作る予定があるとき。
- ロジックとマークアップを1つのJavaScriptの関数にまとめるのが好きで、Next.jsのようなフレームワークを使いたいとき。
- チームが、各レンダリングを追いやすくする明示的でイミュータブルな更新を重視するとき。
次のようなときはVueを選んでください。
- ルーティングとstateのための公式で揃った道具がほしく、決めることを減らしたいとき。
- HTMLテンプレートに慣れたデザイナーやバックエンドの開発者がチームにいるとき。
- サーバーで描画された既存のページに対話性を加えるとき。Vueはscriptタグからページに入れて、そこから育てていけます。
どちらも成熟していて、あらゆる規模の企業が本番で使っています。一方で学んだ概念(コンポーネント、props、導出されたstate、keyの付いたリスト)は、そのままもう一方に活かせます。
よくある質問
VueはReactより簡単ですか?
最初はVueのほうが簡単だと感じる初心者は多いです。テンプレートが普通のHTMLのように見え、リアクティブな値を変える(count.value++)だけでセッターなしに画面が更新されるからです。ReactはJSXと、stateは変更するのではなく置き換えるというルールを学ぶことを求めます。最初の数週間を過ぎれば、難しさは同じくらいです。
ReactとVueはどちらが優れていますか?
一般的にどちらかが勝つということはありません。Reactはルーティングとstateをコミュニティのライブラリに任せ、モバイル向けにReact Nativeがあります。Vueには、まとめて保守される公式のルーター、ストア、開発ツールがあり、多くの人が読みやすいと感じるテンプレートがあります。チームや求人市場が使っているほうを選んでください。
VueのComposition APIとReactのフックの違いは何ですか?
どちらもstateを持つロジックを再利用できる関数にまとめます。Vueの setup はコンポーネントごとに1回だけ実行され、依存関係を自動で追跡するので、依存配列も古いクロージャもありません。Reactのコンポーネント関数はレンダリングのたびに実行されるので、フックは毎回同じ順番で呼ぶ必要があり、エフェクトは依存値を並べます。
VueはJSXを使いますか?
Vueはプラグインを通じてJSXをサポートしていますが、ほとんどのVueのコードは単一ファイルコンポーネント(.vue ファイル)の中のHTMLテンプレートを使います。ReactはほぼどこでもJSXを使います。
VueでNext.jsに相当するものは何ですか?
Nuxtです。ReactにとってのNext.jsと同じように、Vueの上にファイルベースのルーティング、サーバーレンダリング、静的生成、サーバールートを加えます。