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

タイトル: エラー一覧
SEOタイトル: Vue.js よくあるエラー集と対処完全ガイド

この本文には実行可能なタグ等が含まれるため、安全のため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>Failed to mount component</strong> — テンプレート構文ミスまたは props 型不一致</li><li><strong>Cannot read property of undefined</strong> — リアクティビティ未確立、<code>v-if</code> でガード or オプショナルチェイニング</li><li><strong>v-model が動かない</strong> — カスタムコンポーネントで <code>modelValue</code> / <code>update:modelValue</code> emit 漏れ</li><li>Vue 2 → 3 移行: <code>Vue.filter</code> / <code>Vue.prototype</code> / <code>$listeners</code> 廃止</li><li><strong>Vuex → Pinia</strong> 推奨。Vuex 4 は保守モード</li><li>Nuxt の <strong>Hydration mismatch</strong> — SSR と CSR のレンダリング差分。<code><ClientOnly></code> で囲む</li></ul></td></tr></table>
<h2>Vue.js でよく遭遇するエラー一覧</h2>

<p>Vue 3 を中心に、開発で頻出するエラーと対処をまとめます。多くは Vue 2 にも当てはまります。</p>

<h2>1. Failed to mount component</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">[Vue warn]: Failed to mount component: template or render function not defined.
[Vue warn]: Property "xxx" was accessed during render but is not defined on instance.</code></pre>
<p>原因と対処:</p>

<ul>
<li>SFC で <code><template></code> ブロック忘れ → 追加</li>
<li><code>defineComponent</code> 戻り値の <code>template</code> プロパティが未定義 → <code>render</code> または <code>template</code> 追加</li>
<li>動的 import 失敗 → ファイルパス / 拡張子確認</li>
<li>Volar / TypeScript 設定で SFC 認識不能 → <code>tsconfig.json</code> の <code>vueCompilerOptions</code> 確認</li>
</ul>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- ❌ template ブロック無し -->
<script setup>
const msg = &apos;hi&apos;
</script>

<!-- ✅ -->
<script setup>
const msg = &apos;hi&apos;
</script>

<template>
  <div>{{ msg }}</div>
</template></code></pre>
<h2>2. Cannot read properties of undefined (リアクティビティ)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">TypeError: Cannot read properties of undefined (reading &apos;name&apos;)</code></pre>
<p>非同期データの初期値が未定義のままレンダリングされた典型例:</p>
<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 } from &apos;vue&apos;

const user = ref(null)

onMounted(async () => {
  user.value = await fetch(&apos;/api/me&apos;).then(r => r.json())
})
</script>

<template>
  <!-- ❌ user.name で user が null -->
  <div>{{ user.name }}</div>

  <!-- ✅ パターン1: v-if でガード -->
  <div v-if="user">{{ user.name }}</div>

  <!-- ✅ パターン2: オプショナルチェイニング -->
  <div>{{ user?.name }}</div>

  <!-- ✅ パターン3: 初期値を与える -->
  <!-- script 側: const user = ref({ name: &apos;&apos; }) -->
</template></code></pre>
<h2>3. ref / reactive の誤用</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">import { ref, reactive } from &apos;vue&apos;

// ❌ ref を渡されたつもりで生の値を扱う
const count = ref(0)
console.log(count + 1)   // → "[object Object]1"
// ✅
console.log(count.value + 1)

// ❌ reactive オブジェクトを分割代入 → リアクティビティ消失
const state = reactive({ name: &apos;a&apos; })
const { name } = state              // 普通の文字列
name = &apos;b&apos;                          // ❌ state.name は変わらず

// ✅ toRefs で個別 ref 化
import { toRefs } from &apos;vue&apos;
const { name } = toRefs(state)
name.value = &apos;b&apos;                    // ✅ state.name も更新

// ❌ reactive を再代入
let s = reactive({ a: 1 })
s = { a: 2 }                        // ❌ リアクティビティ切断
// ✅ プロパティを更新
s.a = 2
// または ref + .value で全置換
const sref = ref({ a: 1 })
sref.value = { a: 2 }               // ✅</code></pre>
<h2>4. v-model が動かない (カスタムコンポーネント)</h2>

<p>Vue 3 の v-model は <code>modelValue</code> + <code>update:modelValue</code> イベント。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- 親 -->
<MyInput v-model="text" />

<!-- ❌ Vue 2 流儀 (動かない) -->
<script>
export default {
  props: [&apos;value&apos;],
  emits: [&apos;input&apos;],
}
</script>

<!-- ✅ Vue 3 -->
<script setup>
defineProps([&apos;modelValue&apos;])
const emit = defineEmits([&apos;update:modelValue&apos;])
</script>

<template>
  <input :value="modelValue"
         @input="emit(&apos;update:modelValue&apos;, $event.target.value)" />
</template>

<!-- ✅ defineModel (Vue 3.4+, より簡潔) -->
<script setup>
const text = defineModel()
</script>
<template>
  <input v-model="text" />
</template></code></pre>
<h2>5. Vue 2 → 3 移行で消えた API</h2>

<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#fff3e0"><th>Vue 2</th><th>Vue 3 での扱い</th></tr></thead>
<tbody>
<tr><td><code>Vue.filter('xxx', fn)</code></td><td><strong>廃止</strong>。computed や methods で代替</td></tr>
<tr><td><code>Vue.prototype.$xxx</code></td><td><code>app.config.globalProperties.$xxx</code></td></tr>
<tr><td><code>$listeners</code></td><td><code>$attrs</code> に統合</td></tr>
<tr><td><code>v-on.native</code></td><td>不要 (デフォルトでネイティブ)</td></tr>
<tr><td><code>v-model.sync</code></td><td><code>v-model:xxx</code> + 複数 v-model</td></tr>
<tr><td><code>new Vue({...})</code></td><td><code>createApp({...}).mount(...)</code></td></tr>
<tr><td>Functional Component</td><td>関数で書くだけ (props 引数)</td></tr>
<tr><td><code>Vue.set / Vue.delete</code></td><td>不要 (Proxy ベースリアクティビティ)</td></tr>
<tr><td>EventBus (<code>new Vue()</code>)</td><td><code>mitt</code> 等の外部ライブラリ推奨</td></tr>
</tbody>
</table>

<h2>6. Vue Router の動的ルート</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// Vue Router 4
import { createRouter, createWebHistory } from &apos;vue-router&apos;

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: &apos;/users/:id&apos;, component: UserDetail },
    { path: &apos;/users/:id(\\d+)&apos;, component: UserDetail },     // 数値のみ
    { path: &apos;/files/:path(.*)*&apos;, component: FileView },      // catch-all
  ],
})

// 動的ルートで同じコンポーネントが再利用される
// → 同じコンポーネントだとライフサイクル発火しない
// 対処: route.params を watch
import { watch } from &apos;vue&apos;
import { useRoute } from &apos;vue-router&apos;

const route = useRoute()
watch(() => route.params.id, async (newId) => {
  await loadUser(newId)
}, { immediate: true })

// or :key で強制再生成
// <router-view :key="$route.fullPath" /></code></pre>
<h2>7. Pinia (推奨ストア)</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// store/counter.js
import { defineStore } from &apos;pinia&apos;
import { ref, computed } from &apos;vue&apos;

export const useCounterStore = defineStore(&apos;counter&apos;, () => {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  function increment() {
    count.value++
  }
  return { count, double, increment }
})

// コンポーネントから
<script setup>
import { useCounterStore } from &apos;@/store/counter&apos;
import { storeToRefs } from &apos;pinia&apos;

const store = useCounterStore()
const { count, double } = storeToRefs(store)   // リアクティブ維持
const { increment } = store                    // 関数は分割代入 OK
</script></code></pre>
<h2>8. Production 警告を抑制</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-javascript">// main.js
import { createApp } from &apos;vue&apos;
import App from &apos;./App.vue&apos;

const app = createApp(App)

// 開発環境のみ警告ハンドラ
app.config.warnHandler = (msg, instance, trace) => {
  if (msg.includes(&apos;Component name&apos;)) return   // 特定警告を抑制
  console.warn(msg, trace)
}

// 本番では Vue 自体が警告を出さない (vue.runtime.prod.js)
app.mount(&apos;#app&apos;)</code></pre>
<h2>9. Vue DevTools 利用</h2>

<ul>
<li>Chrome 拡張 / Firefox アドオン: 「Vue.js devtools」</li>
<li>スタンドアロン版: <code>npm i -g @vue/devtools</code> → <code>vue-devtools</code> 起動</li>
<li>コンポーネントツリー / Pinia ストア / イベント / Performance を可視化</li>
<li>本番ビルドでは無効化される。デバッグしたい場合は <code>__VUE_PROD_DEVTOOLS__ = true</code></li>
</ul>

<h2>10. Nuxt の Hydration mismatch</h2>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-text">[Vue warn]: Hydration node mismatch:
- Client vnode: <div>...
- Server rendered DOM: <span>...
Hydration completed but contains mismatches.</code></pre>
<p>原因: SSR と CSR で生成される DOM が異なる (時刻、Math.random、window 参照など)。</p>
<pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-vue"><!-- ❌ サーバとクライアントで違う値 -->
<div>{{ new Date().toLocaleString() }}</div>

<!-- ❌ window はサーバに無い -->
<div>{{ window.innerWidth }}</div>

<!-- ✅ ClientOnly で囲む (Nuxt) -->
<ClientOnly>
  <div>{{ new Date().toLocaleString() }}</div>
</ClientOnly>

<!-- ✅ onMounted で window を扱う -->
<script setup>
import { ref, onMounted } from &apos;vue&apos;
const width = ref(0)
onMounted(() => { width.value = window.innerWidth })
</script>

<!-- ✅ useState / useFetch でサーバ側で確定させてから渡す -->
<script setup>
const { data } = await useFetch(&apos;/api/now&apos;)   // SSR で確定
</script></code></pre>
<h2>FAQ</h2>

<p><strong>Q: <code>watch</code> が発火しない</strong><br />
A: ref なら <code>watch(myRef, ...)</code>、reactive 内プロパティは <code>watch(() => state.x, ...)</code> のように関数で。</p>

<p><strong>Q: コンポーネントが再描画されない</strong><br />
A: リアクティビティ切断 (分割代入、再代入)。<code>toRefs</code> や <code>ref</code> 化で解決。</p>

<p><strong>Q: <code>provide / inject</code> がアプリ全体で動かない</strong><br />
A: <code>app.provide(key, value)</code> をルートで設定。コンポーネント内 <code>provide</code> はサブツリーのみ有効。</p>

<p><strong>Q: TypeScript で props の型が効かない</strong><br />
A: <code>defineProps<{ id: number }>()</code> でジェネリック指定。または <code>defineProps({ id: { type: Number, required: true } })</code>。</p>