Welcome to Tech Athletes | テック・アスリート   Click to listen highlighted text! Welcome to Tech Athletes | テック・アスリート

【2026年最新】Vue.js 3完全ガイド|Composition API・TypeScript・Pinia・Nuxt 3まで実践解説

Vue.js 3とは?React・Angularとの違い

Vue.js 3は2020年9月にリリースされた、プログレッシブなJavaScriptフレームワークです。学習曲線が緩やかでありながら、大規模アプリケーション開発にも対応できる柔軟性が特徴です。Vue 3ではComposition APIの導入により、ロジックの再利用性と TypeScriptとの親和性が大幅に向上しました。

特徴Vue.js 3React 18Angular 17
学習難易度低〜中
パフォーマンス高速高速良好
TypeScript対応◎(組み込み)○(追加設定)◎(標準)
公式SSRNuxt 3Next.jsAngular Universal
状態管理PiniaRedux/ZustandNgRx

Vue.js 3環境構築

# create-vue(Viteベース)でプロジェクト作成
npm create vue@latest my-vue-app

# オプション選択例
# ✔ Add TypeScript? › Yes
# ✔ Add JSX Support? › No
# ✔ Add Vue Router? › Yes
# ✔ Add Pinia? › Yes
# ✔ Add Vitest? › Yes
# ✔ Add ESLint? › Yes

cd my-vue-app
npm install
npm run dev

Composition APIの基本

setup()とref・reactive

<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted } from 'vue'

// ref: プリミティブ値のリアクティブ化
const count = ref(0)
const message = ref('Hello Vue 3!')

// reactive: オブジェクトのリアクティブ化
const user = reactive({
  name: '田中太郎',
  age: 30,
  email: 'taro@example.com'
})

// computed: 算出プロパティ
const doubleCount = computed(() => count.value * 2)
const fullProfile = computed(() => `${user.name}(${user.age}歳)`)

// watch: 値の変化を監視
watch(count, (newVal, oldVal) => {
  console.log(`count changed: ${oldVal} → ${newVal}`)
})

// ライフサイクルフック
onMounted(() => {
  console.log('コンポーネントがマウントされました')
})

// メソッド
const increment = () => count.value++
const reset = () => { count.value = 0 }
</script>

<template>
  <div class="counter">
    <h1>{{ message }}</h1>
    <p>カウント: {{ count }} (2倍: {{ doubleCount }})</p>
    <button @click="increment">+1</button>
    <button @click="reset">リセット</button>
    <p>{{ fullProfile }}</p>
  </div>
</template>

コンポーザブル(Composables)でロジックを再利用

Composition APIの真価はコンポーザブル(Composables)によるロジックの再利用です。

// composables/useCounter.ts
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0, step = 1) {
  const count = ref(initialValue)
  
  const doubleCount = computed(() => count.value * 2)
  const isPositive = computed(() => count.value > 0)
  
  const increment = () => (count.value += step)
  const decrement = () => (count.value -= step)
  const reset = () => (count.value = initialValue)
  
  return {
    count,
    doubleCount,
    isPositive,
    increment,
    decrement,
    reset
  }
}

// composables/useFetch.ts
import { ref, onMounted } from 'vue'

export function useFetch<T>(url: string) {
  const data = ref<T | null>(null)
  const loading = ref(true)
  const error = ref<Error | null>(null)
  
  const fetchData = async () => {
    loading.value = true
    try {
      const res = await fetch(url)
      data.value = await res.json()
    } catch (e) {
      error.value = e as Error
    } finally {
      loading.value = false
    }
  }
  
  onMounted(fetchData)
  
  return { data, loading, error, refetch: fetchData }
}

Vue RouterとPiniaの実装

// stores/userStore.ts(Pinia)
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

interface User {
  id: number
  name: string
  email: string
  role: 'admin' | 'user'
}

export const useUserStore = defineStore('user', () => {
  const currentUser = ref<User | null>(null)
  const isLoading = ref(false)
  
  // Getters
  const isLoggedIn = computed(() => currentUser.value !== null)
  const isAdmin = computed(() => currentUser.value?.role === 'admin')
  
  // Actions
  const login = async (email: string, password: string) => {
    isLoading.value = true
    try {
      // APIコール
      const res = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify({ email, password })
      })
      currentUser.value = await res.json()
    } finally {
      isLoading.value = false
    }
  }
  
  const logout = () => {
    currentUser.value = null
  }
  
  return { currentUser, isLoading, isLoggedIn, isAdmin, login, logout }
})

Nuxt 3でSSR・SSG対応

本番環境でのSEO対策やパフォーマンス向上にはNuxt 3を使います。

# Nuxt 3プロジェクト作成
npx nuxi@latest init my-nuxt-app
cd my-nuxt-app
npm install
npm run dev

# ディレクトリ構造
# pages/         → ファイルベースルーティング
# components/    → Vueコンポーネント
# composables/   → 共有ロジック
# server/api/    → APIルート
# public/        → 静的ファイル
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug as string

// データフェッチング(SSR対応)
const { data: post, pending, error } = await useFetch(`/api/posts/${slug}`)

// SEOメタタグ
useHead({
  title: post.value?.title,
  meta: [
    { name: 'description', content: post.value?.excerpt },
    { property: 'og:title', content: post.value?.title },
    { property: 'og:description', content: post.value?.excerpt },
    { property: 'og:image', content: post.value?.thumbnail }
  ]
})
</script>

<template>
  <article v-if="post">
    <h1>{{ post.title }}</h1>
    <div v-html="post.content"></div>
  </article>
  <div v-else-if="pending">読み込み中...</div>
  <div v-else-if="error">エラーが発生しました</div>
</template>

パフォーマンス最適化

<script setup>
import { defineAsyncComponent, shallowRef } from 'vue'

// 遅延ロード(コード分割)
const HeavyComponent = defineAsyncComponent(() =>
  import('./HeavyComponent.vue')
)

// v-memo: リストのメモ化
// v-once: 一度だけレンダリング
</script>

<template>
  <!-- 遅延ロードコンポーネント -->
  <Suspense>
    <HeavyComponent />
    <template #fallback>読み込み中...</template>
  </Suspense>
  
  <!-- 大量リストのメモ化 -->
  <div v-for="item in list" :key="item.id" v-memo="[item.selected]">
    {{ item.name }}
  </div>
</template>

まとめ

Vue.js 3はComposition API・TypeScript対応・Pinia状態管理・Nuxt 3によるSSRと、モダンなWeb開発に必要な要素が揃っています。Reactと比べて学習曲線が緩やかで、特に日本語のコミュニティとドキュメントが充実しているため、日本のエンジニアには非常に学びやすい選択肢です。フロントエンドスキルのアップグレードにぜひVue.js 3をお試しください。

投稿者 kasata

IT企業でエンジニアとして勤務後、テクノロジー情報メディア「Tech Athletes(テック・アスリート)」を運営。プログラミング、クラウドインフラ(AWS/GCP/Azure)、AI活用、Webサービス開発を専門とする。エンジニア・ビジネスパーソン向けに、実際に使ってみた経験をもとに信頼できる技術情報を発信中。資格:AWS認定ソリューションアーキテクト、Python 3 エンジニア認定試験合格。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

Click to listen highlighted text!