はじめに
この記事では、Vue.js(Vue 3)におけるVueインスタンス、データ操作、コンポーネントの利用方法まで解説します。
環境構築は下記記事に記載しているので参考にしてください。

Vue インスタンスとは?
Vue.js の基本単位となるのが「Vue インスタンス」です。Vue インスタンスは、
- データの管理
- イベントの処理
- テンプレートとの連携 を担当し、Vue.js のアプリケーションを動作させる中心的な役割を果たします。
Vue インスタンスの基本構造
Vue 3 では、Vue インスタンスは createApp()
を使って作成します。
App.vue
の基本構造(Vue 3 の書き方)
<script setup>
import { ref } from "vue";
// Vue インスタンスが管理するデータ
const message = ref("こんにちは、Vue.js!");
</script>
<template>
<div id="app">
<!-- テンプレートで data の message を表示 -->
<p>{{ message }}</p>
</div>
</template>
Vue インスタンスの要素
createApp() でインスタンスを作成
main.js
import { createApp } from "vue";
import App from "./App.vue";
// Vue インスタンスを作成し、#app に適用
createApp(App).mount("#app");
setup() でデータを管理
Vue 3 では setup()
内で ref()
を使用してリアクティブなデータを作成します。
Vue 3 の ref()
<script setup>
import { ref } from "vue";
const message = ref("こんにちは、Vue.js!");
<script>
ref()
を使うと、リアクティブなデータを管理できます。
テンプレートとの連携
Vue 3 ではテンプレート内で {{ message }}
を使ってデータを表示できます。
Vue 3 のテンプレート
<template>
<div id="app">
<p>{{ message }}</p>
</div>
</template>
データバインディングの基本
Vue.js では、データバインディングを使ってデータを表示できます。
{{ }} を使ったデータの表示
Vue では マスタッシュ構文 {{ }}
を使うことで、JavaScript の変数を HTML 内に埋め込むことができます。
テンプレートの例
<template>
<div>
<p>{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from "vue";
// データをリアクティブに管理
const message = ref("Vue.js のデータバインディング!");
</script>
ポイント
{{ message }}
は Vue のデータ (message
) を HTML に表示 します。ref()
を使うことで、データがリアクティブに更新されます。
データの変更とリアクティブな動作
Vue.js のデータを変更すると、UI に リアルタイムで反映 されます。
データの変更
<template>
<div>
<p>{{ message }}</p>
<button @click="message = '変更されました!'">変更</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const message = ref("Vue.js のデータバインディング!");
</script>
ポイント
@click="message = '変更されました!'"
により、ボタンをクリックするとmessage
の値が変更され、画面も自動的に更新されます。
属性の動的変更 (v-bind)
Vue では v-bind
を使って、HTML の属性を 動的に変更 できます。
画像の src
を動的に変更
<template>
<div>
<img :src="imageSrc" alt="サンプル画像">
</div>
</template>
<script setup>
import { ref } from "vue";
const imageSrc = ref("https://example.com/sample.jpg");
</script>
ポイント
v-bind:src="imageSrc"
は:src="imageSrc"
に省略できます。imageSrc
の値を変更すると、表示される画像も自動的に更新されます。
Vue.js のディレクティブ
ディレクティブの概要
Vue.js では、ディレクティブ(v-
で始まる特別な属性) を使うことで、HTML 要素に対して 動的な振る舞い を設定できます。
例えば、v-if
を使えば 条件によって要素の表示/非表示 を切り替えたり、v-for
を使えば リストを自動生成 したりできます。
基本の形
<p v-if="isVisible">このメッセージは isVisible が true のとき表示されます。</p>
v-if="条件"
の条件がtrue
なら要素が表示、false
なら削除されます。
条件分岐 (v-if, v-else, v-show)
v-if / v-else の基本
v-if
を使うと、条件によって HTML の要素を追加/削除 できます。
ボタンをクリックしてメッセージを表示/非表示
<template>
<div>
<button @click="showMessage = !showMessage">切り替え</button>
<p v-if="showMessage">こんにちは!</p>
<p v-else>さようなら!</p>
</div>
</template>
<script setup>
import { ref } from "vue";
const showMessage = ref(true);
</script>
v-if vs v-show の違い
ディレクティブ | 動作 | 使い分け |
---|---|---|
v-if | 条件が false のとき DOM から削除 | 頻繁に切り替えない場合に向いている |
v-show | 条件が false のとき CSS(display: none)で非表示 | 頻繁に表示を切り替える場合に向いている |
v-show
の例
<p v-show="showMessage">このメッセージは v-show を使っています。</p>
v-show
は CSS の display: none;
で非表示 にするだけなので、v-if
よりパフォーマンスに優れています。
リストのレンダリング (v-for)
リストを描画するときは v-for
を使います。
配列データをリストとして表示
<template>
<div>
<ul>
<li v-for="(item, index) in items" :key="index">
{{ index + 1 }}: {{ item }}
</li>
</ul>
</div>
</template>
<script setup>
import { ref } from "vue";
const items = ref(["りんご", "バナナ", "ぶどう"]);
</script>
ポイント
v-for="(item, index) in items"
で リストをループ処理:key="index"
を指定することで、Vue が各要素を正しく識別できる
フォーム要素のバインディング (v-model)
v-model
を使うと、フォームの入力値とデータを リアルタイムで同期 できます。
テキスト入力フォーム
<template>
<div>
<input v-model="name" placeholder="名前を入力">
<p>こんにちは、{{ name }} さん!</p>
</div>
</template>
<script setup>
import { ref } from "vue";
const name = ref("");
</script>
入力した名前がリアルタイムで表示されます!
チェックボックス・ラジオボタン・セレクトボックスの例
<template>
<div>
<label>
<input type="checkbox" v-model="checked"> チェックする
</label>
<p>チェック状態: {{ checked }}</p>
<label>
<input type="radio" v-model="gender" value="male"> 男性
</label>
<label>
<input type="radio" v-model="gender" value="female"> 女性
</label>
<p>選択した性別: {{ gender }}</p>
<select v-model="fruit">
<option value="apple">りんご</option>
<option value="banana">バナナ</option>
<option value="grape">ぶどう</option>
</select>
<p>選択した果物: {{ fruit }}</p>
</div>
</template>
<script setup>
import { ref } from "vue";
const checked = ref(false);
const gender = ref("");
const fruit = ref("apple");
</script>
イベントリスナー (v-on, @click)
Vue では v-on
を使って イベント(クリックなど)を処理 できます。
@click
を使ったボタンイベント
<template>
<div>
<button @click="count++">クリック回数: {{ count }}</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
</script>
@click="count++"
でクリックするとcount
を増やすv-on:click
の代わりに@click
を使うのが一般的
関数を使ったイベント処理
<template>
<div>
<button @click="increment">増やす</button>
<p>カウント: {{ count }}</p>
</div>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
const increment = () => {
count.value++;
};
</script>
methods
の代わりに setup
内で関数を定義し、@click="increment"
で呼び出します。
Vue におけるデータ操作の基本
Vue では、以下の 3 つの方法でデータを操作できます。
機能 | 特徴 | 使う場面 |
---|---|---|
メソッド (methods ) | アクションを実行する関数 | ボタンをクリックしたとき などに実行 |
算出プロパティ (computed ) | キャッシュ された計算結果 | リアルタイムで計算が必要なとき |
ウォッチャー (watch ) | データの変化を監視 し処理を実行 | API との連携やデータの監視 |
メソッド (methods) の使い方
methods
を使うと、ボタンをクリックしたとき などのイベントで処理を実行できます。
📌 ボタンをクリックすると、カウントが増える例
<template>
<div>
<p>カウント: {{ count }}</p>
<button @click="increment">増やす</button>
</div>
</template>
<script setup>
import { ref } from "vue";
// カウントのデータ
const count = ref(0);
// メソッドの定義
const increment = () => {
count.value++;
};
</script>
メソッドの特徴
- 呼ばれるたびに実行 される(毎回計算し直す)
- イベント (
@click
) によって処理を実行 するときに適している
引数を使ってデータを操作
<template>
<div>
<p>カウント: {{ count }}</p>
<button @click="increment(2)">+2</button>
<button @click="increment(5)">+5</button>
</div>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
// メソッドに引数を渡す
const increment = (value) => {
count.value += value;
};
</script>
increment(2)
のように引数を渡して処理を変更できます。
算出プロパティ (computed) の活用
computed
は リアクティブなデータを基に自動的に計算 し、その結果をキャッシュする機能です。
文字数を自動計算する例
<template>
<div>
<input v-model="message" placeholder="入力してください">
<p>文字数: {{ charCount }}</p>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const message = ref("");
// 算出プロパティ(computed)
const charCount = computed(() => {
return message.value.length;
});
</script>
charCount
はmessage
が変わると自動で計算される- キャッシュされるため、
message
に変更がない場合は再計算されない
メソッドとの違い
methods | computed | |
---|---|---|
実行のタイミング | 呼ばれるたびに再計算 | データが変わったときのみ再計算(キャッシュあり) |
使う場面 | イベントをトリガーに処理する | 計算結果をキャッシュしたい |
メソッドを使うと、毎回計算される
const charCount = () => {
return message.value.length;
};
→ computed
を使った方が効率的!
ウォッチャー (watch) を使ったデータの監視
watch
を使うと、データの変化を リアルタイムで監視 し、何らかの処理を実行できます。
テキストを入力すると、コンソールにログを出す
<template>
<div>
<input v-model="message" placeholder="入力してください">
<p>入力内容: {{ message }}</p>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
const message = ref("");
// watch でデータの変化を監視
watch(message, (newValue, oldValue) => {
console.log(`変更前: ${oldValue}, 変更後: ${newValue}`);
});
</script>
message
が変わるたびに 新しい値と古い値を取得 して処理できる
検索フォームで API 連携
<template>
<div>
<input v-model="query" placeholder="検索ワードを入力">
<p>検索結果: {{ result }}</p>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
const query = ref("");
const result = ref("");
// データの変更を監視
watch(query, async (newQuery) => {
if (newQuery.length > 2) {
result.value = `「${newQuery}」の検索結果`;
} else {
result.value = "";
}
});
</script>
query
の値が変わるたびに API を呼び出す(今回は簡単なデモ)
Vue.jsにおけるコンポーネント
Vue.js におけるコンポーネントの概念
コンポーネントとは、「再利用可能な UI の部品」 です。
例えば、ボタンやカード、フォームなどを 独立したパーツ にすることで、使い回しや管理がしやすくなります。
コンポーネント指向の開発のメリット
メリット | 説明 |
---|---|
再利用性 | 同じコンポーネントを複数のページや箇所で使える |
保守性の向上 | 各コンポーネントが独立しているため、修正が簡単 |
可読性の向上 | コードが整理され、分かりやすくなる |
例えば、「ボタン」をコンポーネント化すると…
<template>
<button class="my-button">クリック</button>
</template>
<style>
.my-button {
background-color: blue;
color: white;
padding: 10px;
}
</style>
こうすることで、「ボタンを使いたい場所にコンポーネントを呼び出すだけで使える」 ようになります!
コンポーネントの作成方法
グローバルコンポーネントの作成
Vue 3 では、app.component()
を使ってグローバルコンポーネントを登録できます。
ボタンのコンポーネントを作成
<template>
<div id="app">
<my-button></my-button>
</div>
</template>
<script>
import { createApp } from "vue";
const app = createApp({
template: "<my-button></my-button>",
});
// コンポーネントを登録
app.component("my-button", {
template: `<button>クリック</button>`,
});
app.mount("#app");
</script>
シングルファイルコンポーネント(.vue ファイル)の作成
Vue では、.vue
ファイルを使って コンポーネントを分割 できます。
ファイル構成
src/
├── components/
│ ├── MyButton.vue
├── App.vue
├── main.js
MyButton.vue
(ボタンコンポーネント)
<template>
<button>クリック</button>
</template>
App.vue
(親コンポーネント)
<template>
<div>
<MyButton />
</div>
</template>
<script setup>
import MyButton from "./components/MyButton.vue";
</script>
import MyButton from "./components/MyButton.vue";
でコンポーネントを読み込む<MyButton />
でコンポーネントを表示する
親コンポーネントと子コンポーネントの関係
コンポーネントの入れ子構造
Vue のコンポーネントは 親子関係 を作ることができます。
親コンポーネントが子コンポーネントを呼び出す
<template>
<div>
<ChildComponent />
</div>
</template>
<script setup>
import ChildComponent from "./ChildComponent.vue";
</script>
このように、親コンポーネントの中に子コンポーネントを組み込む ことで、アプリを部品ごとに分割できます。
props を使ったデータの受け渡し
props とは?
props
は 親コンポーネントから子コンポーネントへデータを渡す ための仕組みです。
親コンポーネント (App.vue
)
<template>
<div>
<MessageDisplay message="こんにちは!" />
</div>
</template>
<script setup>
import MessageDisplay from "./components/MessageDisplay.vue";
</script>
子コンポーネント (MessageDisplay.vue
)
<template>
<p>{{ message }}</p>
</template>
<script setup>
import { defineProps } from "vue";
// 親コンポーネントから受け取る props を定義
defineProps({
message: String,
});
</script>
props のポイント
defineProps({ message: String })
で受け取るデータを定義- 親コンポーネントで
message="こんにちは!"
を指定すると、子コンポーネントで{{ message }}
として表示される
props の型指定とデフォルト値
Vue 3 では、props
の 型指定 や デフォルト値の設定 ができます。
型指定とデフォルト値を設定
<script setup>
import { defineProps } from "vue";
defineProps({
message: {
type: String,
default: "デフォルトのメッセージ",
},
});
</script>
type: String
→message
は文字列として受け取るdefault: "デフォルトのメッセージ"
→message
が渡されなかった場合のデフォルト値を設定
props を利用する際の注意点
Vue の props
は 単方向データフロー になっています。
単方向データフローとは?
Vue の props
は親コンポーネントから子コンポーネントへ 一方向(単方向) にデータを渡す仕組みです。
つまり、子コンポーネント内で props
の値を直接変更することはできません。
ダメな例
<script setup>
defineProps(["message"]);
// NG: props の値を直接変更しようとするとエラー
message = "変更したい";
</script>
なぜダメなのか?
- Vue では
props
は「読み取り専用」として扱われる。 props
を直接変更すると、Vue の リアクティブシステム に混乱を引き起こし、エラーが発生する可能性がある。
正しい方法(emit を使う)
emit
を使って、親コンポーネントにデータの変更を通知します。
子コンポーネント
<template>
<input v-model="newMessage" />
<button @click="updateMessage">変更</button>
</template>
<script setup>
import { defineProps, defineEmits, ref } from "vue";
const props = defineProps(["message"]); // 親から受け取る
const emit = defineEmits(["update"]); // 親へイベントを送る準備
const newMessage = ref(props.message); // ローカルのリアクティブデータとしてコピー
const updateMessage = () => {
emit("update", newMessage.value); // 親に新しいメッセージを送信
};
</script>
ポイント
props.message
をそのまま変更せずにnewMessage
という ローカルなリアクティブデータ を作成。v-model
を使ってinput
に双方向バインディング。button
クリック時にupdateMessage
を実行し、emit("update", newMessage.value)
で親コンポーネントに新しい値を送る。
親コンポーネント
親コンポーネントでは、子から受け取った update
イベントを処理し、データを更新します。
<template>
<ChildComponent :message="message" @update="handleUpdate" />
<p>現在のメッセージ: {{ message }}</p>
</template>
<script setup>
import { ref } from "vue";
import ChildComponent from "./ChildComponent.vue"; // 子コンポーネントをインポート
const message = ref("初期メッセージ");
const handleUpdate = (newMessage) => {
message.value = newMessage; // 子から受け取った値で更新
};
</script>
親コンポーネントの役割
message
をref
で定義し、ChildComponent
にprops
として渡す。@update="handleUpdate"
を指定し、子コンポーネントからのイベントを受け取る。handleUpdate
メソッドでmessage
を更新し、変更を適用。
動作の流れ
- 親が
message
をprops
として子へ渡す - 子が
props.message
をnewMessage
にコピー - 子で
input
を編集し、updateMessage
をクリック emit("update", newMessage.value)
で親へ通知- 親が
handleUpdate
でmessage
を更新 - 親の
message
が変わると、子のprops.message
も更新される
$refs を使って子コンポーネントを操作
通常は props
や emit
を使ってデータのやり取りを行いますが、$refs
を使うと 親コンポーネントから子コンポーネントのメソッドやデータに直接アクセス できます。
$refs
を使うと、子コンポーネントのメソッドを親コンポーネントから実行 できます。
子コンポーネント (ChildComponent.vue)
<template>
<p>現在のカウント: {{ count }}</p>
</template>
<script setup>
import { ref } from "vue";
const count = ref(0);
// カウントを増やすメソッド
const increment = () => {
count.value++;
};
// メソッドを外部からアクセス可能にする
defineExpose({ increment });
</script>
defineExpose({ increment })
を使うと、親コンポーネントからincrement()
にアクセスできる
親コンポーネント (App.vue)
<template>
<div>
<ChildComponent ref="child" />
<button @click="increaseChildCount">子のカウントを増やす</button>
</div>
</template>
<script setup>
import { ref } from "vue";
import ChildComponent from "./ChildComponent.vue";
const child = ref(null);
// 子コンポーネントのメソッドを実行
const increaseChildCount = () => {
child.value.increment();
};
</script>
<ChildComponent ref="child" />
で 子コンポーネントにアクセスchild.value.increment();
で 子のメソッドを実行
親から子のデータやメソッドを操作できるが、できるだけ props
や emit
を使うのが推奨!
<slot> を使って柔軟なコンポーネントを作る
<slot>
を使うと、親コンポーネントから子コンポーネントの中に自由な内容を挿入 できます。
通常、子コンポーネントの内容は固定ですが、<slot>
を使うと柔軟にコンテンツを挿入 できます。
子コンポーネント (Card.vue)
<template>
<div class="card">
<slot></slot>
</div>
</template>
<style>
.card {
border: 1px solid gray;
padding: 20px;
margin: 10px;
}
</style>
<slot></slot>
の部分に、親から挿入するコンテンツが入る
親コンポーネント (App.vue)
<template>
<Card>
<h2>タイトル</h2>
<p>スロットを使った動的な内容</p>
</Card>
</template>
<script setup>
import Card from "./Card.vue";
</script>
<Card>
の中にh2
やp
を自由に入れられる!
これでコンポーネントの内容を柔軟に変更できる!
名前付きスロット (<slot name="header">)
スロットを複数に分けて、異なる箇所にコンテンツを挿入 できます。
Card.vue
(子コンポーネント)
<template>
<div class="card">
<header><slot name="header"></slot></header>
<main><slot></slot></main>
</div>
</template>
App.vue
(親コンポーネント)
<template>
<Card>
<template #header>
<h2>カードのタイトル</h2>
</template>
<p>本文の内容</p>
</Card>
</template>
#header
を指定すると、<slot name="header">
の部分に挿入される
レイアウトを統一しつつ、コンテンツを柔軟に変更できる!
スコープ付きスロット
子コンポーネントのデータを親に渡し、スロット内で利用できます。
List.vue
(子コンポーネント)
<template>
<ul>
<slot v-for="item in items" :item="item"></slot>
</ul>
</template>
<script setup>
defineProps(["items"]);
</script>
App.vue
(親コンポーネント)
<template>
<List :items="['りんご', 'バナナ', 'ぶどう']">
<template #default="{ item }">
<li>{{ item }}</li>
</template>
</List>
</template>
#default="{ item }"
→ 子のitems
を取得して表示!
まとめ
Vueの基本構文
機能 | 説明 | 使用例 |
---|---|---|
{{ message }} | データをテンプレートに表示 | <p>{{ message }}</p> |
@click="message = '変更'" | データを変更し、UI を更新 | <button @click="message = '変更'"> |
v-bind:src="imageSrc" | 画像や属性を動的に変更 | <img :src="imageSrc"> |
:class="{ active: isActive }" | クラスの適用を動的に制御 | <p :class="{ active: isActive }"> |
:style="{ color: textColor }" | スタイルを動的に変更 | <p :style="{ color: textColor }"> |
ディレクティブ
ディレクティブ | 説明 | 例 |
---|---|---|
v-if / v-else | 条件によって要素を追加・削除 | <p v-if="isVisible">表示</p> |
v-show | CSS の display: none; で非表示 | <p v-show="isVisible">表示</p> |
v-for | 配列のデータをリスト表示 | <li v-for="item in items">{{ item }}</li> |
v-model | フォーム入力とデータを同期 | <input v-model="name"> |
v-on / @click | クリックなどのイベント処理 | <button @click="doSomething"> |
データ操作
機能 | 説明 | 使う場面 |
---|---|---|
methods | イベントで関数を実行 | ボタンクリックで処理を実行 |
computed | キャッシュされた計算結果を返す | リアルタイム計算(文字数など) |
watch | データの変化を監視し処理を実行 | API 連携・データの変更を検知 |
methods
→ イベント発生時に実行computed
→ データをキャッシュして効率的に計算watch
→ データの変更を監視して処理を実行
コンポーネント
項目 | 説明 |
---|---|
コンポーネントとは? | Vue で UI を分割し、再利用可能な部品を作る仕組み |
コンポーネントの作成 | .vue ファイルで作成し、import して使う |
親子コンポーネントの関係 | 親が子を呼び出し、props でデータを渡す |
props | 親→子のデータ受け渡し(型指定・デフォルト値設定可能) |
単方向データフロー | props は親からしか変更できない |
emit | 子→親へデータを渡す |
$refs | 親→子のメソッドやデータを直接操作 |
<slot> | 柔軟なコンテンツの挿入 |
名前付きスロット | #header など特定の箇所に挿入 |
スコープ付きスロット | 子のデータを親に渡す |
コメント