この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: メソッドの定義方法
SEOタイトル: Vue.js メソッド定義完全ガイド(Options API / Composition API / setup)

この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>Options API</strong>: <code>methods: { greet() { ... } }</code> に並べる。<code>this</code> でデータにアクセス可</li><li><strong>Composition API + <code><script setup></code></strong>: <code>const greet = () => { ... }</code> として宣言、<code>this</code> 不要</li><li>テンプレートからの呼出: <code>@click="greet"</code> / 引数付き: <code>@click="greet(&apos;hello&apos;)"</code> / イベントオブジェクト: <code>@click="greet($event)"</code></li><li><strong>computed と methods の違い</strong>: computed はキャッシュされる / methods は毎回実行</li><li>子→親通信は <code>emit(&apos;custom-event&apos;, payload)</code>。<code>defineEmits</code> で型定義</li></ul></td></tr></table>
<h2>Vue.js のメソッド定義 2 つのスタイル</h2>

<p>Vue.js 3 では「Options API」と「Composition API」の 2 つの書き方があり、メソッド定義もそれぞれスタイルが異なります。</p>

<h2>1. Options API でのメソッド定義</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- src/components/Greeting.vue -->
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="greet">挨拶する</button>
    <button @click="greetTo(&apos;世界&apos;)">世界に挨拶</button>
    <input @input="updateMessage" :value="message">
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: &apos;こんにちは&apos;,
    };
  },
  methods: {
    greet() {
      // this でデータと他メソッドにアクセス
      this.message = &apos;やあ!&apos;;
      this.logIt();
    },
    greetTo(name) {
      this.message = `${name}、こんにちは!`;
    },
    updateMessage(event) {
      this.message = event.target.value;
    },
    logIt() {
      console.log(&apos;logged:&apos;, this.message);
    },
  },
};
</script></code></pre>
<h3>Options API のポイント</h3>

<ul>
<li>すべてのメソッドは <code>methods:</code> オブジェクト内に並べる</li>
<li>メソッド内では <strong><code>this</code></strong> で <code>data</code> / <code>props</code> / 他メソッド / <code>computed</code> にアクセス</li>
<li><strong>アロー関数で書いてはいけない</strong>(<code>this</code> が外側の <code>this</code> になってしまう)</li>
<li><strong>非同期処理</strong> (<code>async/await</code>) もそのまま使える</li>
</ul>

<h2>2. Composition API + <code><script setup></code></h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- src/components/Greeting.vue (Composition API) -->
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="greet">挨拶する</button>
    <button @click="greetTo(&apos;世界&apos;)">世界に挨拶</button>
    <input @input="updateMessage" :value="message">
  </div>
</template>

<script setup>
import { ref } from &apos;vue&apos;;

const message = ref(&apos;こんにちは&apos;);

// アロー関数 or 通常 function どちらでも OK
const greet = () => {
  message.value = &apos;やあ!&apos;;
  logIt();
};

function greetTo(name) {
  message.value = `${name}、こんにちは!`;
}

const updateMessage = (event) => {
  message.value = event.target.value;
};

const logIt = () => {
  console.log(&apos;logged:&apos;, message.value);
};
</script></code></pre>
<h3>Composition API のポイント</h3>

<ul>
<li>メソッドは<strong>通常の関数</strong>として <code><script setup></code> 内で宣言</li>
<li><code>this</code> は<strong>存在しない</strong>(不要)</li>
<li>ref/reactive の値には <code>.value</code> でアクセス(テンプレートでは自動アンラップ)</li>
<li>外部公開(テンプレートから使える)は<strong>宣言するだけで自動</strong></li>
<li>TypeScript との相性がよい</li>
</ul>

<h2>Options API vs Composition API 早見表</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>項目</th><th>Options API</th><th>Composition API + setup</th></tr></thead>
<tbody>
<tr><td>メソッド宣言</td><td><code>methods: { greet() {} }</code></td><td><code>const greet = () => {}</code></td></tr>
<tr><td>データ参照</td><td><code>this.count</code></td><td><code>count.value</code></td></tr>
<tr><td><code>this</code></td><td>あり(コンテキスト)</td><td>なし</td></tr>
<tr><td>アロー関数</td><td>NG(this が変わる)</td><td>OK</td></tr>
<tr><td>外部公開</td><td>methods 内に書くだけ</td><td>宣言するだけ</td></tr>
<tr><td>TS 補完</td><td>限定的</td><td>強力</td></tr>
<tr><td>ロジックの再利用</td><td>mixin(推奨されない)</td><td>composables(推奨)</td></tr>
<tr><td>学習コスト</td><td>低</td><td>中</td></tr>
</tbody>
</table>

<h2>テンプレートからの呼び出し方</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><template>
  <!-- 引数なし: 関数参照を渡す -->
  <button @click="greet">A</button>
  <button v-on:click="greet">A(v-on の正式形)</button>

  <!-- 引数あり: 呼び出し式 -->
  <button @click="greet(&apos;hello&apos;)">B</button>

  <!-- イベントオブジェクトを渡す -->
  <input @input="handleInput($event)">

  <!-- 引数 + イベント両方 -->
  <button @click="handleClick(&apos;foo&apos;, $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></code></pre>
<h2>computed と methods と watch の違い</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>用途</th><th>methods</th><th>computed</th><th>watch</th></tr></thead>
<tbody>
<tr><td>計算結果を表示</td><td>毎回再実行</td><td><strong>★ キャッシュされる</strong></td><td>—</td></tr>
<tr><td>クリック等のイベント処理</td><td><strong>★ これが本職</strong></td><td>—</td><td>—</td></tr>
<tr><td>値が変わったときに副作用実行</td><td>—</td><td>—</td><td><strong>★ これが本職</strong></td></tr>
<tr><td>引数を受ける</td><td><strong>○</strong></td><td>○(が再評価されない)</td><td>—</td></tr>
<tr><td>非同期処理</td><td>○</td><td>×(同期のみ)</td><td>○</td></tr>
</tbody>
</table>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><script setup>
import { ref, computed, watch } from &apos;vue&apos;;

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(&apos;count&apos;, String(newVal));
});
</script>

<template>
  <p>{{ formatted() }}</p>   <!-- 関数呼び出し(毎回実行) -->
  <p>{{ doubled }}</p>        <!-- 値として参照(キャッシュ) -->
</template></code></pre>
<h2>子→親通信: emit</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- 子コンポーネント Child.vue -->
<script setup>
// イベント名と型を宣言
const emit = defineEmits([&apos;change&apos;, &apos;submit&apos;]);

const onClick = () => {
  emit(&apos;change&apos;, { id: 1, name: &apos;foo&apos; });   // 親に値を渡す
};
</script>

<template>
  <button @click="onClick">送信</button>
</template>


<!-- 親 Parent.vue -->
<script setup>
import Child from &apos;./Child.vue&apos;;

const onChange = (payload) => {
  console.log(&apos;子から:&apos;, payload);
};
</script>

<template>
  <Child @change="onChange" />
</template></code></pre>
<h2>ライフサイクルとメソッドの組合せ</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><script setup>
import { ref, onMounted, onUnmounted } from &apos;vue&apos;;

const users = ref([]);
const intervalId = ref(null);

const fetchUsers = async () => {
  const res = await fetch(&apos;/api/users&apos;);
  users.value = await res.json();
};

onMounted(() => {
  // 初期ロード
  fetchUsers();
  // 30秒ごとに更新
  intervalId.value = setInterval(fetchUsers, 30000);
});

onUnmounted(() => {
  // アンマウント時にタイマー解除
  clearInterval(intervalId.value);
});
</script></code></pre>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e8f5e9"><th>Options API</th><th>Composition API</th><th>タイミング</th></tr></thead>
<tbody>
<tr><td><code>beforeCreate</code></td><td><code>setup()</code> 直前</td><td>インスタンス作成前</td></tr>
<tr><td><code>created</code></td><td><code>setup()</code> 内</td><td>インスタンス作成済 / DOM 前</td></tr>
<tr><td><code>beforeMount</code></td><td><code>onBeforeMount</code></td><td>DOM 挿入直前</td></tr>
<tr><td><code>mounted</code></td><td><code>onMounted</code></td><td>DOM 挿入後(API 呼出はここ)</td></tr>
<tr><td><code>beforeUpdate</code></td><td><code>onBeforeUpdate</code></td><td>再レンダリング前</td></tr>
<tr><td><code>updated</code></td><td><code>onUpdated</code></td><td>再レンダリング後</td></tr>
<tr><td><code>beforeUnmount</code></td><td><code>onBeforeUnmount</code></td><td>破棄直前</td></tr>
<tr><td><code>unmounted</code></td><td><code>onUnmounted</code></td><td>破棄完了</td></tr>
</tbody>
</table>

<h2>composables(ロジックの再利用)</h2>

<p>Composition API の真価。メソッド + 状態をまとめて切り出し、他のコンポーネントで再利用できます:</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// composables/useCounter.js
import { ref, computed } from &apos;vue&apos;;

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 };
}</code></pre><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- 任意のコンポーネントで使用 -->
<script setup>
import { useCounter } from &apos;@/composables/useCounter&apos;;

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></code></pre>
<h2>FAQ</h2>

<p><strong>Q: Options API と Composition API はどちらを使うべき?</strong><br />
A: 公式は<strong>新規プロジェクトでは Composition API を推奨</strong>。ただし Options API も将来も使えます。チームに合わせて選択。</p>

<p><strong>Q: <code>this</code> がうまく動かない</strong><br />
A: Options API の methods 内で<strong>アロー関数を使っていない</strong>か確認してください。<code>methods: { greet: () => { this.x } }</code> は NG。<code>methods: { greet() { this.x } }</code> が正しい。</p>

<p><strong>Q: <code>@click="greet"</code> と <code>@click="greet()"</code> の違い</strong><br />
A: 前者は<strong>関数参照を渡す</strong>(イベントオブジェクトが第一引数に入る)、後者は<strong>その場で呼び出して戻り値を渡す</strong>。引数を渡したいときだけ括弧を付けます。</p>