タイトル: メソッドの定義方法
SEOタイトル: Vue.js メソッド定義完全ガイド(Options API / Composition API / setup)
| この記事の要点 |
|
Vue.js のメソッド定義 2 つのスタイル
Vue.js 3 では「Options API」と「Composition API」の 2 つの書き方があり、メソッド定義もそれぞれスタイルが異なります。
1. Options API でのメソッド定義
<!-- src/components/Greeting.vue -->
<template>
<div>
<p>{{ message }}</p>
<button @click="greet">挨拶する</button>
<button @click="greetTo('世界')">世界に挨拶</button>
<input @input="updateMessage" :value="message">
</div>
</template>
<script>
export default {
data() {
return {
message: 'こんにちは',
};
},
methods: {
greet() {
// this でデータと他メソッドにアクセス
this.message = 'やあ!';
this.logIt();
},
greetTo(name) {
this.message = `${name}、こんにちは!`;
},
updateMessage(event) {
this.message = event.target.value;
},
logIt() {
console.log('logged:', this.message);
},
},
};
</script>
Options API のポイント
- すべてのメソッドは
methods:オブジェクト内に並べる - メソッド内では
thisでdata/props/ 他メソッド /computedにアクセス - アロー関数で書いてはいけない(
thisが外側のthisになってしまう) - 非同期処理 (
async/await) もそのまま使える
2. Composition API + <script setup>
<!-- src/components/Greeting.vue (Composition API) -->
<template>
<div>
<p>{{ message }}</p>
<button @click="greet">挨拶する</button>
<button @click="greetTo('世界')">世界に挨拶</button>
<input @input="updateMessage" :value="message">
</div>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('こんにちは');
// アロー関数 or 通常 function どちらでも OK
const greet = () => {
message.value = 'やあ!';
logIt();
};
function greetTo(name) {
message.value = `${name}、こんにちは!`;
}
const updateMessage = (event) => {
message.value = event.target.value;
};
const logIt = () => {
console.log('logged:', message.value);
};
</script>
Composition API のポイント
- メソッドは通常の関数として
<script setup>内で宣言 thisは存在しない(不要)- ref/reactive の値には
.valueでアクセス(テンプレートでは自動アンラップ) - 外部公開(テンプレートから使える)は宣言するだけで自動
- TypeScript との相性がよい
Options API vs Composition API 早見表
| 項目 | Options API | Composition API + setup |
|---|---|---|
| メソッド宣言 | methods: { greet() {} } | const greet = () => {} |
| データ参照 | this.count | count.value |
this | あり(コンテキスト) | なし |
| アロー関数 | NG(this が変わる) | OK |
| 外部公開 | methods 内に書くだけ | 宣言するだけ |
| TS 補完 | 限定的 | 強力 |
| ロジックの再利用 | mixin(推奨されない) | composables(推奨) |
| 学習コスト | 低 | 中 |
テンプレートからの呼び出し方
<template>
<!-- 引数なし: 関数参照を渡す -->
<button @click="greet">A</button>
<button v-on:click="greet">A(v-on の正式形)</button>
<!-- 引数あり: 呼び出し式 -->
<button @click="greet('hello')">B</button>
<!-- イベントオブジェクトを渡す -->
<input @input="handleInput($event)">
<!-- 引数 + イベント両方 -->
<button @click="handleClick('foo', $event)">C</button>
<!-- 修飾子 (.prevent, .stop, .once, .capture, .self) -->
<form @submit.prevent="onSubmit">...</form>
<a @click.stop="onClick">伝播止める</a>
<!-- キー修飾子 -->
<input @keyup.enter="onEnter">
<input @keyup.esc="onCancel">
<!-- ボタン修飾子 -->
<div @click.right="onRightClick">右クリック</div>
</template>
computed と methods と watch の違い
| 用途 | methods | computed | watch |
|---|---|---|---|
| 計算結果を表示 | 毎回再実行 | ★ キャッシュされる | — |
| クリック等のイベント処理 | ★ これが本職 | — | — |
| 値が変わったときに副作用実行 | — | — | ★ これが本職 |
| 引数を受ける | ○ | ○(が再評価されない) | — |
| 非同期処理 | ○ | ×(同期のみ) | ○ |
<script setup>
import { ref, computed, watch } from 'vue';
const count = ref(0);
// methods(呼び出すたびに実行)
const formatted = () => `現在: ${count.value}`;
// computed(依存値が変わったときだけ再計算、結果はキャッシュ)
const doubled = computed(() => count.value * 2);
// watch(変化に応じて副作用)
watch(count, (newVal, oldVal) => {
console.log(`変化: ${oldVal} → ${newVal}`);
localStorage.setItem('count', String(newVal));
});
</script>
<template>
<p>{{ formatted() }}</p> <!-- 関数呼び出し(毎回実行) -->
<p>{{ doubled }}</p> <!-- 値として参照(キャッシュ) -->
</template>
子→親通信: emit
<!-- 子コンポーネント Child.vue -->
<script setup>
// イベント名と型を宣言
const emit = defineEmits(['change', 'submit']);
const onClick = () => {
emit('change', { id: 1, name: 'foo' }); // 親に値を渡す
};
</script>
<template>
<button @click="onClick">送信</button>
</template>
<!-- 親 Parent.vue -->
<script setup>
import Child from './Child.vue';
const onChange = (payload) => {
console.log('子から:', payload);
};
</script>
<template>
<Child @change="onChange" />
</template>
ライフサイクルとメソッドの組合せ
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const users = ref([]);
const intervalId = ref(null);
const fetchUsers = async () => {
const res = await fetch('/api/users');
users.value = await res.json();
};
onMounted(() => {
// 初期ロード
fetchUsers();
// 30秒ごとに更新
intervalId.value = setInterval(fetchUsers, 30000);
});
onUnmounted(() => {
// アンマウント時にタイマー解除
clearInterval(intervalId.value);
});
</script>
| Options API | Composition API | タイミング |
|---|---|---|
beforeCreate | setup() 直前 | インスタンス作成前 |
created | setup() 内 | インスタンス作成済 / DOM 前 |
beforeMount | onBeforeMount | DOM 挿入直前 |
mounted | onMounted | DOM 挿入後(API 呼出はここ) |
beforeUpdate | onBeforeUpdate | 再レンダリング前 |
updated | onUpdated | 再レンダリング後 |
beforeUnmount | onBeforeUnmount | 破棄直前 |
unmounted | onUnmounted | 破棄完了 |
composables(ロジックの再利用)
Composition API の真価。メソッド + 状態をまとめて切り出し、他のコンポーネントで再利用できます:
// composables/useCounter.js
import { ref, computed } from 'vue';
export function useCounter(initial = 0) {
const count = ref(initial);
const increment = () => count.value++;
const decrement = () => count.value--;
const reset = () => count.value = initial;
const isZero = computed(() => count.value === 0);
const doubled = computed(() => count.value * 2);
return { count, increment, decrement, reset, isZero, doubled };
}<!-- 任意のコンポーネントで使用 -->
<script setup>
import { useCounter } from '@/composables/useCounter';
const { count, increment, decrement, reset, doubled } = useCounter(10);
</script>
<template>
<p>カウント: {{ count }}(2 倍: {{ doubled }})</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<button @click="reset">リセット</button>
</template>
FAQ
Q: Options API と Composition API はどちらを使うべき?
A: 公式は新規プロジェクトでは Composition API を推奨。ただし Options API も将来も使えます。チームに合わせて選択。
Q: this がうまく動かない
A: Options API の methods 内でアロー関数を使っていないか確認してください。methods: { greet: () => { this.x } } は NG。methods: { greet() { this.x } } が正しい。
Q: @click="greet" と @click="greet()" の違い
A: 前者は関数参照を渡す(イベントオブジェクトが第一引数に入る)、後者はその場で呼び出して戻り値を渡す。引数を渡したいときだけ括弧を付けます。