🪽

Next.jsだけ知ってる人間がVueについて学んでみた

に公開

TL;DR

  • Vue 3.5 が 2024‑09 にリリース。内部最適化と defineModel などの改善が入った minor 版。
  • Next.js 15.3 (2025‑04‑17) は Turbopack がほぼ完成し、React Server Components の扱いが議論中。
  • JSX 脳からテンプレート脳へ: () => <div>{{count}}</div> から <template><div>{{ count }}</div></template> へ。
  • Composition API と React Hooks は「関数で状態をカプセル化する」という点で同型だが、実装は reactivity system と state queue で異なる。
  • Nuxt 3 はファイルベースルーティングと Nitro によるハイブリッドレンダリングを実装。
  • 状態管理は Pinia が Redux/Zustand と同じ「central store + useStore」の DX。
  • TypeScript × Volar で IDE 体験は React/TypeScript と遜色なし。
  • 開発時には Vue Devtools / Nuxt DevTools を活用。

目次

  1. はじめに
  2. Vue エコシステム概観
  3. テンプレート構文 vs JSX
  4. Composition API と Hooks の違い
  5. Single‑File Component とディレクトリ設計
  6. ルーティング & データフェッチ
  7. 状態管理 (Pinia) vs Redux/Zustand
  8. SSR / SSG / ハイブリッドレンダリング
  9. 型安全と IDE 体験
  10. テスト戦略とツール
  11. パフォーマンス最適化 Tips
  12. 移行チェックリスト
  13. まとめ & 所感
  14. 付録: よくあるハマりどころ FAQ

はじめに

Next.js (React) で日々開発してきた筆者が、Vue 3.5Nuxt 3 をキャッチアップした際の「頭の切り替えポイント」をまとめた比較記事です。比較対象は Next.js 15.x (App Router + React Server Components がデフォルト) とし、コードは TypeScript ベースで記載しています。

対象読者

  • React & Next.js の経験がある
  • Vue をまだ触ったことがない、あるいは少し触っただけ
  • フレームワークの内部アーキテクチャや DX に興味がある

Vue エコシステム概観

要素 Vue 周辺 React/Next.js 周辺
UI ライブラリ Vue 3.5 core, <script setup> React 18 concurrent, Server Components
フルスタック FW Nuxt 3 (Nitro, hybrid) Next.js 15 (App Router)
状態管理 Pinia (Vuex 5 相当) Redux Toolkit, Zustand
ビルドツール Vite 5 (dev) / Nitro (prod) Turbopack (dev) / Webpack 5
DevTools Vue Devtools, Nuxt DevTools React DevTools, Next.js overlay

Vue は official router, official state management, official SSR が揃っており「フレームワーク標準」が厚い。React は逆に「選択肢が豊富」。


テンプレート構文 vs JSX

// React / Next.js
export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      {count}
    </button>
  );
}
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>

<template>
  <button @click="count++">{{ count }}</button>
</template>

JSX からテンプレートへの翻訳早見表

JSX Vue テンプレート
className class
onClick={fn} @click="fn"
style={{ color: 'red' }} :style="{ color: 'red' }"
cond && <A/> <A v-if="cond" />

Composition API と Hooks の違い

項目 Composition API React Hooks
根本原理 Proxy ベース reactivity State queue & reconciliation
状態更新 ref / reactive (mutable) useState (immutable)
再評価粒度 依存単位 コンポーネント単位

watchEffectuseEffect に近いが依存を自動収集するため deps 配列が不要です。


Single‑File Component とディレクトリ設計

  • .vue<template>, <script setup>, <style scoped> を内包
  • CSS Modules は <style module> を利用
  • デフォルトでローカル CSS スコープがあるためグローバルリークを防止しやすい

例 (Nuxt 3):

pages/
  index.vue
  users/
    [id].vue
components/
  ui/
    Button.vue
  layout/
    Header.vue

ルーティング & データフェッチ

ルーティング比較

フレームワーク 動的ルート グループ化
Nuxt 3 [id].vue なし
Next.js 15 [id]/page.tsx (group) ディレクトリ

データフェッチ

Next.js Nuxt 3
クライアント useSWR, RSC fetch useFetch, useAsyncData
サーバ Server Actions, ランタイム再検証 defineNuxtRouteMiddleware, API route

状態管理 (Pinia) vs Redux/Zustand

Pinia は store オブジェクトがそのままリアクティブなので、selector を追加で書く必要がありません。また defineStore による型推論で DX が高いです。


SSR / SSG / ハイブリッドレンダリング

トピック Next.js Nuxt 3
SSG generateStaticParams + export nuxi generate
SSR デフォルト (streaming) nuxi build + Node/Edge
Edge Vercel Edge Runtime Nitro preset (Cloudflare 等)

型安全と IDE 体験

  • Volar により <template> でも型エラーが赤線表示される
  • defineModel<T>() で双方向バインディングも型安全
  • Nuxt DevTools は Pinia store の値をそのまま確認できる

テスト戦略とツール

レイヤ React/Next.js Vue/Nuxt
ユニット Vitest, Jest Vitest, Vue Test Utils
コンポーネント Testing Library Testing Library/vue
E2E Playwright, Cypress Playwright, Cypress

パフォーマンス最適化 Tips

  1. v-memo (Vue 3.4) で再レンダリング制御
  2. 自動バッチングは Vue がデフォルト実装
  3. Vite 5 + Rollup による ESM 分割でバンドルサイズ削減

移行チェックリスト

  • React Router → Vue Router (useParamsuseRoute().params)
  • Context → Provide/Inject または Pinia
  • Suspense/ErrorBoundary → <Suspense> / <ErrorBoundary>
  • CSS‑in‑JS → <style scoped>、UnoCSS など

まとめ & 所感

React 脳から Vue へはテンプレート構文に慣れることが最大の壁。しかし Composition API と Pinia によりロジック層の移植は容易でした。Vite + Volar + DevTools の DX は非常に高く、学習コストを払う価値があります。


付録: よくあるハマりどころ FAQ

症状 対処
value of undefined ref / reactive のスコープを確認
vue-router catch error 動的 import コンポーネントで defineProps を忘れていないか
HMR で Pinia store が初期化 acceptHMRUpdate を追記

参考リンク

Discussion