Vue

初級 | 8分 で読める | 2026.04.24

公式ドキュメント

概要

Vue 3 は、テンプレート構文と Composition API を使って UI を組み立てる JavaScript フレームワークです。このチートシートでは、リアクティビティ、テンプレート、ライフサイクル、props / emit、Composables、Router、Pinia、フォームをまとめます。

最初に覚える最小セット

項目API / 構文
状態ref / reactive
派生値computed
監視watch / watchEffect
イベント@click
属性:src / v-bind
条件v-if / v-else
リストv-for + :key
双方向入力v-model

Vue では、テンプレート内は自動アンラップされますが、script 内の ref.value が必要です。props は読み取り専用で、子から親へ変更を伝える場合は emit を使います。

リアクティビティ基礎

API説明
ref(value)プリミティブ値をリアクティブ化
reactive(obj)オブジェクトをリアクティブ化
computed(() => ...)算出プロパティ
watch(source, callback)監視
watchEffect(() => ...)自動依存追跡の監視
<script setup>
import { ref, reactive, computed } from 'vue'

const count = ref(0)
const user = reactive({
 name: 'Alice',
 age: 25
})

const doubled = computed(() => count.value * 2)

function increment() {
 count.value++
}
</script>

<template>
 <p>{{ count }} - {{ doubled }}</p>
 <button @click="increment">+1</button>
 <p>{{ user.name }} ({{ user.age }})</p>
</template>

ポイント: refは.valueでアクセス、reactiveはそのままアクセスします。テンプレート内では.valueが自動でアンラップされます。

テンプレート構文

構文説明
{{ message }}テキスト補間
v-bind:src="url" / :src="url"属性バインディング
v-on:click="handler" / @click="handler"イベントリスナー
v-if="condition"条件付きレンダリング
v-for="item in items"リストレンダリング
v-model="value"双方向バインディング
<template>
 <img :src="imageUrl" :alt="title">
 <button @click="handleClick">Click</button>
 
 <div v-if="isVisible">表示中</div>
 <div v-else>非表示</div>

 <ul>
 <li v-for="item in items" :key="item.id">
 {{ item.name }}
 </li>
 </ul>

 <input v-model="text" type="text">
</template>

実践メモ: v-forには必ず:keyを指定します。一意なキーがないと、リストの更新時にパフォーマンスが劣化したり、状態が崩れたりします。

ライフサイクルフック

フックタイミング
onBeforeMountマウント前
onMountedマウント後(DOM 操作可能)
onBeforeUpdate更新前
onUpdated更新後
onBeforeUnmountアンマウント前
onUnmountedアンマウント後
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

const count = ref(0)
let timer = null

onMounted(() => {
 console.log('Component mounted')
 timer = setInterval(() => {
 count.value++
 }, 1000)
})

onUnmounted(() => {
 console.log('Component unmounted')
 clearInterval(timer)
})
</script>

ポイント: onMountedで DOM が利用可能になります。API 呼び出しや DOM 操作はここで実行します。onUnmountedでタイマーやイベントリスナーをクリーンアップします。

コンポーネント Props と Emit

<!-- Child.vue -->
<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
 title: String,
 count: {
 type: Number,
 default: 0
 }
})

const emit = defineEmits(['increment', 'decrement'])

function handleClick() {
 emit('increment', props.count + 1)
}
</script>

<template>
 <h2>{{ title }}</h2>
 <p>{{ count }}</p>
 <button @click="handleClick">+1</button>
</template>
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const count = ref(0)

function onIncrement(newValue) {
 count.value = newValue
}
</script>

<template>
 <Child title="My Counter" :count="count" @increment="onIncrement" />
</template>

注意: propsは読み取り専用です。子コンポーネントから直接変更できません。親に変更を通知する場合はemitを使います。

Composables(再利用ロジック)

// useCounter.js
import { ref } from 'vue'

export function useCounter(initialValue = 0) {
 const count = ref(initialValue)

 function increment() {
 count.value++
 }

 function decrement() {
 count.value--
 }

 return {
 count,
 increment,
 decrement
 }
}
<script setup>
import { useCounter } from './useCounter'

const { count, increment, decrement } = useCounter(10)
</script>

<template>
 <p>{{ count }}</p>
 <button @click="increment">+</button>
 <button @click="decrement">-</button>
</template>

実践メモ: Composablesはuseプレフィックスの関数で、状態とロジックを再利用できます。複数コンポーネントで同じロジックを使う場合に便利です。

Vue Router

API説明
<router-link to="/path">ナビゲーションリンク
<router-view />ルートコンポーネント表示
useRouter()ルーターインスタンス取得
useRoute()現在のルート情報取得
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from './views/Home.vue'
import About from './views/About.vue'

const routes = [
 { path: '/', component: Home },
 { path: '/about', component: About },
 { path: '/user/:id', component: User }
]

export const router = createRouter({
 history: createWebHistory(),
 routes
})
<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

function goToAbout() {
 router.push('/about')
}

console.log(route.params.id)
</script>

<template>
 <nav>
 <router-link to="/">Home</router-link>
 <router-link to="/about">About</router-link>
 </nav>
 <router-view />
</template>

ポイント: useRouterでナビゲーション操作、useRouteでパラメータ取得します。router.pushプログラムから遷移するときに使います

Pinia(状態管理)

// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCounterStore = defineStore('counter', () => {
 const count = ref(0)
 const doubled = computed(() => count.value * 2)

 function increment() {
 count.value++
 }

 return {
 count,
 doubled,
 increment
 }
})
<script setup>
import { useCounterStore } from './stores/counter'

const counter = useCounterStore()
</script>

<template>
 <p>{{ counter.count }} - {{ counter.doubled }}</p>
 <button @click="counter.increment">+1</button>
</template>

実践メモ: Piniaは Vuex の後継で、Composition API と同じ記法で書けます。型推論も強力で、TypeScript との相性が良いです。

フォームバインディング

要素v-model の動作
<input type="text">value 属性をバインド
<textarea>value 属性をバインド
<input type="checkbox">checked 属性をバインド
<input type="radio">checked 属性をバインド
<select>value 属性をバインド
<script setup>
import { ref } from 'vue'

const text = ref('')
const checked = ref(false)
const selected = ref('')
</script>

<template>
 <input v-model="text" type="text">
 <input v-model="checked" type="checkbox">
 <select v-model="selected">
 <option value="A">Option A</option>
 <option value="B">Option B</option>
 </select>
</template>
修飾子説明
v-model.lazychange イベントで同期(input ではなく)
v-model.number数値型に自動変換
v-model.trim前後の空白を削除

ポイント: v-model.numberを使うと、input[type="number"]の値が文字列ではなく数値になります。計算処理で型変換が不要になります

クラスとスタイルバインディング

<template>
 <div :class="{ active: isActive, 'text-danger': hasError }">
 テキスト
 </div>

 <div :class="[baseClass, { active: isActive }]">
 配列形式
 </div>

 <div :style="{ color: textColor, fontSize: fontSize + 'px' }">
 スタイル
 </div>
</template>

実践メモ: :classのオブジェクト構文で、条件に応じてクラスを付け外しできます。静的クラスと併用も可能です。

スロット

<!-- Card.vue -->
<template>
 <div class="card">
 <div class="card-header">
 <slot name="header">デフォルトヘッダー</slot>
 </div>
 <div class="card-body">
 <slot>デフォルト本文</slot>
 </div>
 </div>
</template>
<!-- 使用側 -->
<Card>
 <template #header>
 <h2>タイトル</h2>
 </template>
 <p>本文テキスト</p>
</Card>

ポイント: スロットでコンポーネントの一部を親から注入できます。nameで複数のスロットを定義できます。

まとめ

Vue 3 では、ref / reactivecomputedwatch、テンプレート構文、props / emit、Composables を押さえると基本的な画面を組み立てられます。Router と Pinia は、ルーティングや共有状態が必要になってから導入します。

.value の有無、v-for:key、props の読み取り専用ルールはつまずきやすい点です。詳細は公式ガイドで確認してください。

参考リソース

関連記事

← 一覧に戻る
PR
PR
PR
PR