◀ 11.

Vue.js よくあるエラー集と対処完全ガイド

▶
この記事の要点
  • Failed to mount component — template/renderや読み込んだVueビルドを確認。propsの型警告とは区別
  • Cannot read property of undefined — リアクティビティ未確立、v-if でガード or オプショナルチェイニング
  • v-model が動かない — カスタムコンポーネントで modelValue / update:modelValue emit 漏れ
  • Vue 2 → 3 移行: Vue.filter / Vue.prototype / $listeners 廃止
  • Vuex → Pinia 推奨。Vuex 4 は保守モード
  • Nuxt の Hydration mismatch — SSR と CSR のレンダリング差分。<ClientOnly> で囲む

Vue.js でよく遭遇するエラー一覧

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

この一覧には失敗例と修正例、別ファイルの断片を同じコード枠に並べた比較があります。複数のscript setupを1つの.vueへ貼り付ける手順ではありません。採用する修正例だけを対象ファイルへ入れてください。

1. Failed to mount component

[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.

上の2つの警告は同じ原因とは限りません。template/render未定義の警告はVue 2等で見られるもので、Vue 3の未定義プロパティ警告とは分けて読みます。Viteのruntime-only構成でDOM内テンプレートを使っていないかも確認してください。

原因と対処:

  • SFC で <template> ブロック忘れ → 追加
  • defineComponent 戻り値の template プロパティが未定義 → render または template 追加
  • 動的 import 失敗 → ファイルパス / 拡張子確認
  • Volar / TypeScript 設定で SFC 認識不能 → tsconfig.json の vueCompilerOptions 確認

失敗例(template/renderがないコンポーネント):

<!-- ❌ template ブロック無し -->
<script setup>
const msg = 'hi'
</script>

修正例(こちらだけを1つの.vueへ保存):

<script setup>
const msg = 'hi'
</script>

<template>
  <div>{{ msg }}</div>
</template>

2. Cannot read properties of undefined (リアクティビティ)

TypeError: Cannot read properties of undefined (reading 'name')

非同期データの初期値が未定義のままレンダリングされた典型例:

<script setup>
import { ref, onMounted } from 'vue'

const user = ref(null)

onMounted(async () => {
  user.value = await fetch('/api/me').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: '' }) -->
</template>

3. ref / reactive の誤用

reactiveから分割代入した文字列は元のstateと連動せず、toRefsの参照はstateの同じプロパティへつながる比較図

constへの再代入や同名変数の重複宣言はJavaScriptのエラーです。reactiveの分割代入による連動の喪失とは分けて確認します。以下は変数名を分け、各状態をConsoleで比較できる例です。

import { ref, reactive, toRefs } from 'vue';

// ref: JavaScriptでは.valueを読む。
const count = ref(0);
console.log(count + 1); // 誤った読み方。数値の1にはならない。
console.log(count.value + 1); // 1

// 分割代入した文字列は、元のstateと連動しない。
const state = reactive({ name: 'a' });
let { name: detachedName } = state;
detachedName = 'b';
console.log(detachedName, state.name); // b a

// toRefsなら同じプロパティを更新できる。
const { name: nameRef } = toRefs(state);
nameRef.value = 'b';
console.log(state.name); // b

// オブジェクトの再代入は、元のProxyを購読する処理と切り離される。
let s = reactive({ a: 1 });
const originalProxy = s;
s = { a: 2 }; // 失敗例: sは通常のオブジェクトになる。
console.log(s.a, originalProxy.a); // 2 1
originalProxy.a = 2; // 正しい例: 元のProxyのプロパティを更新。

// オブジェクト全体を置き換えたいならrefを使う。
const sref = ref({ a: 1 });
sref.value = { a: 2 };
console.log(sref.value.a); // 2

4. v-model が動かない (カスタムコンポーネント)

Vue 3 の v-model は modelValue + update:modelValue イベント。

親コンポーネント側の呼び出し(textは親で定義):

<!-- 親 -->
<MyInput v-model="text" />

Vue 3の標準v-modelとは合わないvalue/input宣言の例(子の断片):

<script>
export default {
  props: ['value'],
  emits: ['input'],
}
</script>

子MyInput.vueの修正例:

<script setup>
defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
</script>

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

Vue 3.4以降なら上の子コンポーネントの代わりにdefineModelを使えます。2つの実装を同じファイルへ重ねないでください:

<script setup>
const text = defineModel()
</script>
<template>
  <input v-model="text" />
</template>

5. Vue 2 → 3 移行で消えた API

Vue 2Vue 3 での扱い
Vue.filter('xxx', fn)廃止。computed や methods で代替
Vue.prototype.$xxxapp.config.globalProperties.$xxx
$listeners$attrs に統合
@click.native.nativeは廃止。emitsにないリスナーはルート要素へ渡る。複数ルートやinheritAttrs:falseは明示的な転送が必要
:title.sync="pageTitle"v-model:title="pageTitle"(prop:title、emit:update:title)。複数のv-modelも可能
new Vue({...})createApp({...}).mount(...)
Functional Component関数で書くだけ (props 引数)
Vue.set / Vue.delete不要 (Proxy ベースリアクティビティ)
EventBus (new Vue())mitt 等の外部ライブラリ推奨

6. Vue Router の動的ルート

// Vue Router 4
import { createRouter, createWebHistory } from 'vue-router'

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

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

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

// or :key で強制再生成
// <router-view :key="$route.fullPath" />

7. Pinia (推奨ストア)

ストアのファイル:

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

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

コンポーネント側のscript setup(別ファイルの断片):

<script setup>
import { useCounterStore } from '@/store/counter'
import { storeToRefs } from 'pinia'

const store = useCounterStore()
const { count, double } = storeToRefs(store)   // リアクティブ維持
const { increment } = store                    // 関数は分割代入 OK
</script>

8. Production 警告を抑制

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// warnHandlerは開発用。原因調査を終える前に警告を消さない。
app.config.warnHandler = (msg, instance, trace) => {
  console.warn(msg, trace)
}

// 本番では Vue 自体が警告を出さない (vue.runtime.prod.js)
app.mount('#app')

9. Vue DevTools 利用

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

10. Nuxt の Hydration mismatch

[Vue warn]: Hydration node mismatch:
- Client vnode: <div>...
- Server rendered DOM: <span>...
Hydration completed but contains mismatches.

原因: SSR と CSR で生成される DOM が異なる (時刻、Math.random、window 参照など)。

<!-- ❌ サーバとクライアントで違う値 -->
<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 'vue'
const width = ref(0)
onMounted(() => { width.value = window.innerWidth })
</script>

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

FAQ

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

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

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

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

エラーを調べる順番

  1. 最初のConsoleエラーを読み、構文エラー・Vueの警告・通信エラーを分けます。
  2. 失敗例と修正例を混ぜず、1ファイルへ入れるコードを確認します。
  3. データ取得前のnull、空配列、取得後の値を別々に確認します。
  4. refなら.value、reactiveなら元のProxyのプロパティが更新されたかを確認します。
  5. Vue 2とVue 3のprops・emit・ルーターの対象版を確認します。

参照: Vueのリアクティビティ、v-modelと.syncの移行、.native廃止とemits(2026年10月確認)。

Post Share
子ページ
  1. Error in created hook: "ReferenceError: axios is not defined"
同階層のページ
  1. インストール(ファイルのダウンロード)
  2. npmを使用したプロジェクトの作成
  3. for 繰り返し処理
  4. ifの条件分岐とtemplateを用いたグループ化
  5. クリック時のイベント処理(on:click)
  6. modelとdata フォーム入力値とDOMへの即時反映
  7. computed(算出プロパティ)と使い方とdataとの違い
  8. ライフサイクルフック(created / mounted / updated / destroyedの使い方)
  9. $nextTickの使い方(ライフサイクルフック)
  10. メソッドの定義方法
  11. エラー一覧
  12. ルーティング設定
  13. aリンクの貼り方と動的URLの作成
  14. Mixinを利用した共通処理の記述方法
  15. v-bindによるデータ連携
  16. ヘッダー/フッターの共通コンポーネント
  17. ナビゲーションの現在ページをハイライトする方法
  18. 画面サイズの取得方法