ずんだもん「ReactとVue.jsを初心者目線で比較してみたのだ！サンプルコード付きで違いがすぐ分かるのだ」

**ずんだもん**「ぼくはずんだもん。Web技術なんも分からんおじさんエンジニアなのだ！」

## ある日のふたり

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>

**ずんだもん**「ずん子ちゃん、最近『React』と【Vue.js』ってよく聞くけど、どっちが美味しいのだ？」

**東北ずん子**「ずんだもん、それは“ずんだもち”と間違えてるよ！でも、どっちを学ぶか迷う人が多いのは本当だよね。」

**ずんだもん**「そうなのだ！もし“ずんだ.js”があれば即決なのに…。でも現実はReactかVue.jsなのだ！」

**東北ずん子**「じゃあ、迷えるずんだもんのために、強みや弱みを表で分かりやすくまとめてみたよ♪」

# 比較表

**東北ずん子**「それぞれの特徴や学習難易度を表にしてみたから、まずは見てみてね♪」

| 項目          | React                                                                                                      | Vue.js                                                                                               |
| ----------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **強み**      | - 世界最大のシェア  <br>- 求人・案件が豊富  <br>- Meta社のサポート  <br>- エンタープライズ実績  <br>- 豊富なエコシステム  <br>- React Nativeでモバイル対応 | - 学習コストが低い  <br>- HTML/CSSの知識で始めやすい  <br>- 日本語情報が豊富  <br>- 日本コミュニティが強い  <br>- 開発効率が高い  <br>- シンプルな構文 |
| **弱み**      | - 学習難易度が高い  <br>- JSXやHooksなど独自概念  <br>- 状態管理が複雑  <br>- 初心者には敷居が高い                                         | - エコシステムが小さい  <br>- グローバル採用率が低い  <br>- 大規模企業での実績が少なめ                                                 |
| **学習難易度**   | JavaScriptの知識必須。JSXやHooksなどの理解が必要。習得まで2-3ヶ月（基礎）                                                            | HTML/CSSの知識でOK。直感的な記述。習得まで1-2ヶ月（基礎）                                                                  |
| **パフォーマンス** | 大規模向き。最適化技術が豊富。仮想DOMで高速。                                                                                   | 小中規模向き。初期ロードやメモリ効率が高い。仮想DOMで高速。                                                                      |
| **求人・市場価値** | 世界的に高い。                                                                                                    | 日本国内で高い。                                                                                             |
| **将来性**     | 国際的な案件・大規模開発で需要拡大                                                                                          | 日本国内や中小規模案件で安定需要                                                                                     |

**ずんだもん**「こうやって比べると、どっちも魅力があるのだ！自分に合った方を選ぶのが大事だね！」


## サンプルコード

## サンプル1：ボタンの処理

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>


**東北ずん子**「ずんだもん、ReactとVue.jsって書き方もけっこう違うんだよ。サンプルで見てみよう！」

**ずんだもん**「ボタンを押すとカウントアップするやつ、どんな風に書くのか気になるのだ！」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/61645c02-f298-41b9-aef4-6e2fcaf8a0e9.png)


## React（関数コンポーネント＋Hooks）

```.jsx
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        カウントアップ
      </button>
    </div>
  );
}

export default Counter;
```

## Vue.js（Composition API）

```.html
<template>
  <div>
    <p>カウント: {{ count }}</p>
    <button @click="count++">カウントアップ</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>

```

## 書き方の主な違い

|項目|React|Vue.js|
|---|---|---|
|**UI定義**|JSX（JavaScript内でHTML風記述）|テンプレート（HTMLに近い構文）|
|**状態管理**|useStateフック|refでリアクティブ変数|
|**イベント**|onClick（キャメルケース）|@click（HTML属性風）|
|**構造**|全てJS内で完結|テンプレート＋スクリプト分離|

**ずんだもん**「Vue.jsの方がHTMLっぽい書き方だし、Reactは全部JavaScriptで書く感じなんだね～！」


## サンプル2：テキスト入力とリアルタイム表示

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>


**ずんだもん**「ずん子ちゃん、入力フォームで文字を打ったら、すぐ下に表示されるやつも見てみたいのだ！」

**東北ずん子**「もちろん！バインディングの仕組みが分かるサンプルだよ♪」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/7aeff801-6e84-46a4-b566-6812c79f04f6.png)


## React

```.jsx
import React, { useState } from 'react';

function TextInput() {
  const [text, setText] = useState('');
  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={e => setText(e.target.value)}
        placeholder="入力してね"
      />
      <p>入力内容: {text}</p>
    </div>
  );
}

export default TextInput;

```

## Vue.js

```.html
<template>
  <div>
    <input v-model="text" type="text" placeholder="入力してね" />
    <p>入力内容: {{ text }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const text = ref('');
</script>

```

**ずんだもん**「Vue.jsのv-modelは簡単で分かりやすいのだ！ReactはonChangeで値を更新するんだね～」

## サンプル3：リストのレンダリング

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>

**東北ずん子**「次は配列のリストを画面に並べるサンプルだよ。よく使うから覚えておくと便利だよ！」

**ずんだもん**「リスト表示はどっちもよく使うのだ！」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/2de97150-37a3-4837-aa02-871802567271.png)


## React

```.jsx
import React from 'react';

function ItemList() {
  const items = ['ずんだ', 'もち', 'ずんだもち'];
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>
      ))}
    </ul>
  );
}

export default ItemList;

```

## Vue.js

```.html
<template>
  <ul>
    <li v-for="(item, index) in items" :key="index">{{ item }}</li>
  </ul>
</template>

<script setup>
const items = ['ずんだ', 'もち', 'ずんだもち'];
</script>

```


**ずんだもん**「Vue.jsはv-forで、Reactはmapでリストを作るんだね！どっちもkeyが大事なのだ！」


## サンプル4：条件分岐（if文による表示切り替え）

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>


**ずんだもん**「ずん子ちゃん、条件によって表示を変える方法も知りたいのだ！」

**東北ずん子**「OK！たとえば「ログインしているかどうか」で表示を切り替えるサンプルを用意したよ♪」

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/dee322dc-f930-4760-b445-2bb0027d76b5.png"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/cbd59163-e3fe-481c-8b8d-2cc99a5855c2.png" alt="Image 2"></td>
  </tr>
</table>

## React

```.jsx
import React, { useState } from 'react';

function LoginStatus() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <div>
      <button onClick={() => setIsLoggedIn(!isLoggedIn)}>
        {isLoggedIn ? 'ログアウト' : 'ログイン'}
      </button>
      <p>
        {isLoggedIn ? 'ようこそ、ずんだもん！' : 'ログインしてください'}
      </p>
    </div>
  );
}

export default LoginStatus;

```

## Vue.js

```.html
<template>
  <div>
    <button @click="isLoggedIn = !isLoggedIn">
      {{ isLoggedIn ? 'ログアウト' : 'ログイン' }}
    </button>
    <p>
      <span v-if="isLoggedIn">ようこそ、ずんだもん！</span>
      <span v-else>ログインしてください</span>
    </p>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const isLoggedIn = ref(false);
</script>

```

**ずんだもん**「Vue.jsはv-if/v-elseで、Reactは三項演算子で条件分岐するんだね！」


## サンプル5：親子コンポーネントの連携（親→子へのprops、子→親へのイベント）

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>

**東北ずん子**「最後は親子コンポーネントのやり取りだよ！親から子にデータを渡したり、子から親にイベントを伝えたりするのは実務でもよく使うよ！」

## React

### Parent.jsx

```.jsx
import React, { useState } from 'react';
import Child from './Child';

function Parent() {
  const [message, setMessage] = useState('こんにちは！');

  const handleReply = (reply) => {
    setMessage(reply);
  };

  return (
    <div>
      <h2>親コンポーネント</h2>
      <p>子からのメッセージ: {message}</p>
      <Child onReply={handleReply} />
    </div>
  );
}

export default Parent;

```

### Child.jsx

```.jsx
import React from 'react';

function Child({ onReply }) {
  return (
    <div>
      <h3>子コンポーネント</h3>
      <button onClick={() => onReply('ずんだもんからこんにちは！')}>
        親にメッセージを送る
      </button>
    </div>
  );
}

export default Child;

```


## Vue.js

### Parent.vue

```.html
<template>
  <div>
    <h2>親コンポーネント</h2>
    <p>子からのメッセージ: {{ message }}</p>
    <Child @reply="handleReply" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import Child from './Child.vue';

const message = ref('こんにちは！');
function handleReply(reply) {
  message.value = reply;
}
</script>

```

### Child.vue

```.html
<template>
  <div>
    <h3>子コンポーネント</h3>
    <button @click="sendReply">親にメッセージを送る</button>
  </div>
</template>

<script setup>
const emit = defineEmits(['reply']);

function sendReply() {
  emit('reply', 'ずんだもんからこんにちは！');
}
</script>

```

**ずんだもん**「Reactはpropsで関数を渡して、Vue.jsはイベント（emit）で親に伝えるんだね～！」

**東北ずん子**「そうだよ！どちらも親子間のやり取りはとても大事だから、しっかり覚えておこうね♪」

## サンプル6：Graph API

<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>


**ずんだもん**「ずん子ちゃん、GraphAPIを使ってMicrosoft 365の情報を取得したいのだ！ReactやVue.jsでどうやって使うの？」

**東北ずん子**「GraphAPIはREST APIだから、アクセストークンを取得してHTTPリクエストを送ればOKだよ。ここでは、サインイン済みのユーザー情報（/me）を取得するサンプルを紹介するね！」


## React

```.jsx
import React, { useEffect, useState } from 'react';

function GraphMe({ accessToken }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('https://graph.microsoft.com/v1.0/me', {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    })
      .then(res => res.json())
      .then(data => setUser(data));
  }, [accessToken]);

  if (!user) return <div>Loading...</div>;

  return (
    <div>
      <h2>ユーザー情報</h2>
      <p>名前: {user.displayName}</p>
      <p>メール: {user.mail}</p>
    </div>
  );
}

export default GraphMe;

```

- accessTokenはMicrosoft認証で取得したトークンを渡してください。



## Vue.js

```.html
<template>
  <div v-if="user">
    <h2>ユーザー情報</h2>
    <p>名前: {{ user.displayName }}</p>
    <p>メール: {{ user.mail }}</p>
  </div>
  <div v-else>Loading...</div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const props = defineProps(['accessToken']);
const user = ref(null);

onMounted(async () => {
  const res = await fetch('https://graph.microsoft.com/v1.0/me', {
    headers: {
      Authorization: `Bearer ${props.accessToken}`,
    },
  });
  user.value = await res.json();
});
</script>

```

- こちらもaccessTokenをpropsで受け取る形にしています。

**ずんだもん**「どっちもfetchでAPIを呼んでるのだ！アクセストークンをちゃんと用意して、Authorizationヘッダーにつけるのがポイントなんだね！」

**東北ずん子**「そうだよ。GraphAPIは他にも色々なリソースが取得できるから、エンドポイントを変えればTeamsやメール、カレンダー情報も取れるよ。Graph ExplorerやPostmanで試してみるのもおすすめだよ！」

**東北ずん子**「こうやって比べると、似てるところも違うところもあって面白いよね♪」

**ずんだもん**「サンプルを見ながら自分で書いてみると、もっと理解が深まるのだ～！」

## 技術選択の指針（まとめ）
<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>

**ずんだもん**「ずん子ちゃん、結局どっちを選べばいいのか迷っちゃうのだ…」

**東北ずん子**「プロジェクトやチームの状況によってオススメが違うから、表でまとめてみたよ！」

|チーム・プロジェクト状況|推奨フレームワーク|理由|
|---|---|---|
|大規模・国際案件|React|エコシステム・実績・求人・将来性が強い|
|中小規模・日本国内|Vue.js|学習しやすく、開発効率が高い、日本語情報が豊富|
|初心者・HTML/CSS中心|Vue.js|直感的で始めやすい|
|JS経験豊富・グローバル志向|React|市場価値・応用範囲が広い|

**ずんだもん**「自分の目標やチームの状況に合わせて選ぶのがポイントなのだ！」

## まとめ

**東北ずん子**「最後に、もう一度ポイントをまとめるね！」

::: note
- **React**は「世界標準」「大規模・高収入・グローバル向け」
    
- **Vue.js**は「学習しやすい」「日本国内・中小規模・初心者向け」
    
- どちらも今後も需要が高く、キャリアやプロジェクトに合わせて選択するのがベストです。
:::
    
<table>
  <tr>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/c0f938e8-bc8c-40c7-8ba6-db76fe6fe150.png" alt="Image 1" width="150"></td>
    <td><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/40ea5f5b-72da-453c-8c50-4fcc4309150e.png" alt="Image 2" width="160"></td>
  </tr>
</table>

**ずんだもん**「どっちも覚えておくと、きっと役に立つのだ！ずん子ちゃん、ありがとうなのだ～！」

**東北ずん子**「どういたしまして♪ みんなも自分に合ったフレームワークで楽しく開発してね！」

**ずんだもん**「ちなみにぼくは“ずんだもち.js”を開発したのだ！使うと画面が全部緑色になって、バグを発見すると“ずんだアラート”が鳴るのだ！」

**東北ずん子**「それ、デバッグしてる時に『ずんだずんだずんだ〜♪』って音楽が流れるやつ？」

**ずんだもん**「そうなのだ！でもみんな音楽に合わせて踊るから、もっとバグが増えちゃったのだ！…本末転倒なのだ！」

**東北ずん子**「それはもはやバグじゃなくて仕様だね（笑）」

## 利用キャラクター

- ずんだもん：[VOICEVOX:ずんだもん](https://voicevox.hiroshiba.jp/)

本記事で使用しているキャラクター画像の著作権は、それぞれの権利者に帰属します。
非商用目的での利用に基づき掲載しています。