Vue.js入門:Composition APIでモダンなフロントエンド開発を始めよう
目次
Vue 3とは
Vue.jsはEvan You氏が開発したJavaScriptフレームワークです。2020年にリリースされたVue 3ではComposition APIが導入され、ロジックの再利用性・TypeScriptとの親和性が大幅に向上しました。
ReactとVueを比較すると、以下のような特徴があります。
| 項目 | Vue 3 | React 18 |
|---|---|---|
| テンプレート記法 | HTML拡張構文(.vueファイル) | JSX |
| リアクティブ | ref / reactive | useState / useReducer |
| 学習コスト | 比較的低い | 中程度 |
| 公式ルーター | Vue Router | React Router(サードパーティ) |
| 状態管理 | Pinia | Redux / Zustand |
| 採用企業 | Alibaba, Nintendo, GitLab | Meta, Airbnb, Netflix |
Vueは「テンプレートとロジックが同じファイル(SFC: Single File Component)に収まる」という書きやすさが特徴で、チーム全員がJSXを知らなくても参加しやすいです。
プロジェクトのセットアップ
Vue 3の公式推奨ツールはcreate-vueです(ViteベースでVue公式のスキャフォールドツール)。
# プロジェクト作成
npm create vue@latest my-vue-app
# 対話式で設定を選択
# ✔ Add TypeScript? → Yes
# ✔ Add Vue Router? → Yes
# ✔ Add Pinia? → Yes(状態管理が必要な場合)
# ✔ Add ESLint? → Yes
cd my-vue-app
npm install
npm run dev
# http://localhost:5173 で起動
src/ディレクトリ構成は以下が標準です。
src/
├── components/ # 再利用可能なUIコンポーネント
├── views/ # ページ単位のコンポーネント
├── router/ # Vue Routerの設定
├── stores/ # Piniaストア
├── composables/ # Composition API関数(ロジック再利用)
└── App.vue # ルートコンポーネント
テンプレート構文の基本
Vue SFC(Single File Component)は<template>・<script>・<style>の3ブロックで構成されます。
<template>
<div class="container">
<!-- 変数の展開 -->
<p>{{ message }}</p>
<!-- v-bind: 属性バインディング(省略形は :) -->
<img :src="imageUrl" :alt="altText" />
<!-- v-on: イベントバインディング(省略形は @) -->
<button @click="handleClick">クリック</button>
<!-- v-if / v-else: 条件レンダリング -->
<p v-if="isLoggedIn">ようこそ!</p>
<p v-else>ログインしてください</p>
<!-- v-for: リストレンダリング(:keyは必須) -->
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<!-- v-model: 双方向バインディング -->
<input v-model="inputValue" placeholder="入力してください" />
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const message = ref('Hello, Vue 3!')
const imageUrl = ref('/img/logo.png')
const altText = ref('ロゴ')
const isLoggedIn = ref(false)
const items = ref([
{ id: 1, name: 'りんご' },
{ id: 2, name: 'バナナ' },
])
const inputValue = ref('')
function handleClick() {
alert('クリックされました!')
}
</script>
<script setup>はVue 3のSyntax Sugarで、setup()関数の返り値を自動的にテンプレートに公開します。
Composition APIのリアクティブ
ref — プリミティブ値のリアクティブ
import { ref } from 'vue'
const count = ref(0)
const name = ref('Vue')
// 値を読む・書くには .value を使う(テンプレート内は不要)
console.log(count.value) // 0
count.value++ // 1
reactive — オブジェクトのリアクティブ
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'あくえり',
age: 25,
}
})
// reactive はオブジェクトそのまま参照できる(.value 不要)
state.count++
state.user.name = '新しい名前'
computed — 算出プロパティ
import { ref, computed } from 'vue'
const price = ref(1000)
const quantity = ref(3)
// 依存する値が変わったときのみ再計算される
const total = computed(() => price.value * quantity.value)
console.log(total.value) // 3000
price.value = 2000
console.log(total.value) // 6000
watch — 値の変更を監視
import { ref, watch } from 'vue'
const searchQuery = ref('')
// searchQueryが変わるたびに実行
watch(searchQuery, (newValue, oldValue) => {
console.log(`検索: ${oldValue} → ${newValue}`)
// API呼び出しなど非同期処理を行う
})
コンポーネント設計:props と emit
親から子へはprops、子から親へはemitでデータを渡します。
子コンポーネント(TodoItem.vue)
<template>
<li class="todo-item">
<input
type="checkbox"
:checked="todo.completed"
@change="emit('toggle', todo.id)"
/>
<span :class="{ completed: todo.completed }">{{ todo.text }}</span>
<button @click="emit('remove', todo.id)">削除</button>
</li>
</template>
<script setup lang="ts">
// Props定義
const props = defineProps<{
todo: {
id: number
text: string
completed: boolean
}
}>()
// Emit定義
const emit = defineEmits<{
toggle: [id: number]
remove: [id: number]
}>()
</script>
<style scoped>
.completed {
text-decoration: line-through;
color: #94a3b8;
}
</style>
親コンポーネント(TodoApp.vue)
<template>
<div class="todo-app">
<h1>Todoアプリ</h1>
<!-- 新規追加フォーム -->
<form @submit.prevent="addTodo">
<input v-model="newTodoText" placeholder="Todoを入力..." />
<button type="submit">追加</button>
</form>
<!-- 統計 -->
<p>{{ remainingCount }}件が未完了 / 合計{{ todos.length }}件</p>
<!-- Todoリスト -->
<ul>
<TodoItem
v-for="todo in todos"
:key="todo.id"
:todo="todo"
@toggle="toggleTodo"
@remove="removeTodo"
/>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import TodoItem from './TodoItem.vue'
interface Todo {
id: number
text: string
completed: boolean
}
const todos = ref<Todo[]>([
{ id: 1, text: 'Vue 3を学ぶ', completed: false },
{ id: 2, text: 'Composition APIを理解する', completed: true },
])
const newTodoText = ref('')
let nextId = 3
// 算出プロパティ
const remainingCount = computed(
() => todos.value.filter(t => !t.completed).length
)
// Todo追加
function addTodo() {
const text = newTodoText.value.trim()
if (!text) return
todos.value.push({ id: nextId++, text, completed: false })
newTodoText.value = ''
}
// 完了切り替え
function toggleTodo(id: number) {
const todo = todos.value.find(t => t.id === id)
if (todo) todo.completed = !todo.completed
}
// 削除
function removeTodo(id: number) {
todos.value = todos.value.filter(t => t.id !== id)
}
</script>
Composable — ロジックの再利用
Composition APIの大きなメリットはロジックを関数として切り出せる点です。
// src/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<string | null>(null)
async function fetchData() {
try {
loading.value = true
const response = await fetch(url)
if (!response.ok) throw new Error(`HTTP error: ${response.status}`)
data.value = await response.json()
} catch (e) {
error.value = e instanceof Error ? e.message : '取得に失敗しました'
} finally {
loading.value = false
}
}
onMounted(fetchData)
return { data, loading, error, refetch: fetchData }
}
使う側のコンポーネントはこのように呼び出すだけです。
<script setup lang="ts">
import { useFetch } from '@/composables/useFetch'
const { data, loading, error } = useFetch<{ name: string }[]>(
'https://api.example.com/users'
)
</script>
<template>
<div>
<p v-if="loading">読み込み中...</p>
<p v-else-if="error">エラー: {{ error }}</p>
<ul v-else>
<li v-for="user in data" :key="user.name">{{ user.name }}</li>
</ul>
</div>
</template>
Vue.js 3 フロントエンド開発の教科書
Vue 3 + TypeScriptの実践的な使い方をComposition APIを中心に解説。Pinia・Vue Routerも網羅しており、現場レベルのVue開発スキルを習得できます。
※ アフィリエイトリンクを含みます
まとめ
Vue 3 + Composition APIの要点を整理します。
ref: プリミティブ値をリアクティブに。値アクセスは.valuereactive: オブジェクトをリアクティブに。.value不要computed: 依存値が変わった時だけ再計算watch: 値の変化を監視して副作用を実行- props/emit: 親子コンポーネント間のデータフロー
- Composable: ロジックを関数に切り出して再利用
<script setup>構文を使うと記述量が減り、TypeScriptとの統合もスムーズです。まずは公式ガイドのチュートリアルでTodoアプリを作り、その後でVue Routerによるページ遷移、Piniaによる状態管理に進むのがおすすめの学習ルートです。