Vue ライフサイクルフック
Vue のライフサイクルフック(Lifecycle hooks)は、コンポーネントのライフサイクルにおける特定のステージで、任意の処理(コード)を実行するために用意された仕組みです。
1. ライフサイクルフックとは
コンポーネントが新しいステージに到達するたびに、特定の関数が実行されます。開発者はその関数にコードを記述することで、そのステージに「フック(接続)」することができます。
以下は、コンポーネントが持つすべてのライフサイクルフックです。
beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmountederrorCapturedrenderTrackedrenderTriggeredactivateddeactivatedserverPrefetch
それぞれのフックの実装例を以下に示します。
2. 'beforeCreate' フック
beforeCreate ライフサイクルフックは、コンポーネントが初期化される前に実行されます。つまり、Vue がコンポーネントの data、算出プロパティ(computed)、メソッド、イベントリスナーをセットアップする前の段階です。
このフックは、例えばグローバルなイベントリスナーの設定などに利用できますが、コンポーネント自身に属する要素(データ、ウォッチャー、メソッドなど)はこの時点ではまだ作成されていないため、アクセスを避ける必要があります。
また、DOM 要素もコンポーネントが mounted されるまで作成されないため、この段階で DOM にアクセスすることもできません。
2.1 実装例
CompOne.vue:
<template>
<h2>コンポーネント</h2>
<p>これはコンポーネントです</p>
<p id="pResult">{{ text }}</p>
</template>
<script>
export default {
data() {
return {
text: '...'
}
},
beforeCreate() {
this.text = '初期テキスト'; // この行は効果がありません
console.log("beforeCreate: コンポーネントはまだ作成されていません。");
}
}
</script>App.vue:
<template>
<h1>'beforeCreate' ライフサイクルフック</h1>
<p>'beforeCreate' フックからの console.log() メッセージは確認できますが、Vue のデータプロパティを変更しようとした処理は反映されません。なぜなら、この段階ではデータプロパティがまだ作成されていないからです。</p>
<button @click="this.activeComp = !this.activeComp">コンポーネントの追加/削除</button>
<div>
<comp-one v-if="activeComp"></comp-one>
</div>
</template>
<script>
export default {
data() {
return {
activeComp: false
}
}
}
</script>
<style>
#app > div {
border: dashed black 1px;
border-radius: 10px;
padding: 10px;
margin-top: 10px;
background-color: lightgreen;
}
#pResult {
background-color: lightcoral;
display: inline-block;
}
</style>上記の例において、CompOne.vue の 15行目は効果がありません。これは Vue のデータプロパティ内のテキストを変更しようとしていますが、そのプロパティ自体がまだ存在しないためです。ブラウザのコンソールを開いて、16行目の console.log() の結果を確認してください。
3. 'created' フック
created ライフサイクルフックは、コンポーネントが初期化された直後に実行されます。この段階では、Vue による data、算出プロパティ、メソッド、イベントリスナーのセットアップが完了しています。
ただし、DOM 要素はコンポーネントが mounted されるまでアクセスできないため、この段階で DOM 操作を行うのは避けてください。
created フックは、HTTP リクエストによるデータのフェッチや、データの初期値設定によく使われます。
3.1 実装例
CompOne.vue:
<template>
<h2>コンポーネント</h2>
<p>これはコンポーネントです</p>
<p id="pResult">{{ text }}</p>
</template>
<script>
export default {
data() {
return {
text: '...'
}
},
created() {
this.text = '初期テキスト';
console.log("created: コンポーネントが作成されました。");
}
}
</script>4. 'beforeMount' フック
beforeMount ライフサイクルフックは、コンポーネントが mounted される直前、つまり DOM に追加される直前に実行されます。
この段階でもまだ DOM 要素にはアクセスできません。
4.1 実装例
CompOne.vue:
<template>
<h2>コンポーネント</h2>
<p>これはコンポーネントです</p>
<p ref="pEl" id="pEl">'beforeMount' フックからこのテキストにアクセスを試みます。</p>
</template>
<script>
export default {
beforeMount() {
console.log("beforeMount: コンポーネントがマウントされる直前です。");
// この段階では 'pEl' DOM 要素に到達できないため、エラーが発生します
this.$refs.pEl.innerHTML = "ハロー・ワールド!";
}
}
</script>5. 'mounted' フック
コンポーネントが DOM ツリーに追加された直後に、mounted() 関数が呼び出されます。
ここが、ref 属性や $refs オブジェクトを使用してコンポーネントの DOM 要素に関連する処理(フォーカス設定やサードパーティ製ライブラリの初期化など)を行える最初のタイミングです。
5.1 実装例
CompOne.vue:
<template>
<h2>コンポーネント</h2>
<p>このコンポーネントが DOM に追加された直後に mounted() 関数が呼ばれます。この例では、マウント後にアラートが表示されます。</p>
<p><strong>注意:</strong> コンポーネントが表示される前にアラートが見えるのは、ブラウザがコンポーネントを画面にレンダリングする前にアラートが実行されるためです。</p>
</template>
<script>
export default {
mounted() {
alert("コンポーネントがマウントされました!");
}
}
</script>補足: mounted ステージはコンポーネントが DOM に追加された後に発生しますが、上記の例ではコンポーネントが表示される前に alert() が見えます。これは、コンポーネントが DOM に追加された後、ブラウザが画面を描画(レンダリング)する前に mounted ステージが実行され、alert() がブラウザの処理を一時停止させるためです。
5.2 実践的な例:入力フィールドへのフォーカス
フォームコンポーネントがマウントされた直後に、ユーザーがすぐに入力できるようにカーソルを input フィールドに移動させる例です。
CompOne.vue:
<template>
<h2>フォームコンポーネント</h2>
<p>DOM に追加された際、mounted() 関数内で入力要素にフォーカスを当てます。</p>
<form @submit.prevent>
<label>
<p>
名前: <br>
<input type="text" ref="inpName">
</p>
</label>
<label>
<p>
年齢: <br>
<input type="number">
</p>
</label>
<button>送信</button>
</form>
</template>
<script>
export default {
mounted() {
// ref を使って input にフォーカス
this.$refs.inpName.focus();
}
}
</script>6. 'beforeUpdate' フック
beforeUpdate ライフサイクルフックは、コンポーネントのデータに変更があった際、その更新が画面にレンダリングされる前に呼び出されます。
このフックの特徴は、新しいアップデートをトリガーすることなくアプリケーションに変更を加えられる点です。これにより、無限ループを回避できます。逆に、後述の updated フックでデータを変更すると無限ループが発生する可能性があるため注意が必要です。
6.1 実装例
App.vue:
<script>
export default {
data() {
return {
activeComp: true
}
},
beforeUpdate() {
// ログ用のリストに項目を追加
this.$refs.divLog.innerHTML += "<li>beforeUpdate: 'updated' フックの直前に実行されました。</li>";
}
}
</script>7. 'updated' フック
updated ライフサイクルフックは、コンポーネントが DOM ツリーを更新した後に呼び出されます。
7.1 実装例
App.vue:
updated() {
console.log("コンポーネントがアップデートされました!");
}重要: updated フック内で自身のステート(データ)を変更しないように注意してください。データを変更すると再び updated が呼ばれ、無限ループに陥ります。
7.2 無限ループの例(非推奨)
以下の例では、updated 内でテキストを更新し、それが原因で再度ページが更新されるため、無限ループが発生します。
App.vue:
updated() {
this.text += "hi, "; // データの変更 -> 更新 -> updated 実行 -> データの変更... のループ
}8. 'beforeUnmount' フック
beforeUnmount ライフサイクルフックは、コンポーネントが DOM から削除される直前に呼び出されます。この段階では、まだ DOM 要素にアクセス可能です。
8.1 実装例
CompOne.vue:
beforeUnmount() {
alert("beforeUnmount: pタグ内のテキストは: " + this.$refs.pEl.innerHTML);
}9. 'unmounted' フック
unmounted ライフサイクルフックは、コンポーネントが DOM から完全に削除された後に呼び出されます。
このフックは、イベントリスナーの削除、タイマー(setTimeout/setInterval)のキャンセルなど、メモリリークを防ぐためのクリーンアップ処理によく使われます。
9.1 実装例
CompOne.vue:
unmounted() {
alert("コンポーネントが削除(アンマウント)されました!");
}10. 'errorCaptured' フック
errorCaptured ライフサイクルフックは、子または子孫コンポーネントでエラーが発生したときに呼び出されます。エラーハンドリングやログ出力、ユーザーへのエラー表示に利用されます。
この関数は以下の3つの引数を受け取ります。
- error: エラーオブジェクト
- compInst: エラーを発生させたコンポーネントインスタンス
- errorInfo: エラーのソースタイプなどの情報
10.1 実装例
App.vue:
errorCaptured(error, compInst, errorInfo) {
console.log("error: ", error);
console.log("compInst: ", compInst);
console.log("errorInfo: ", errorInfo);
return false; // エラーの伝播を止める場合は false を返す
}11. 'renderTracked' と 'renderTriggered'
これらのフックはデバッグ用であり、開発モード(development mode)でのみ利用可能です。
- renderTracked: リアクティブな依存関係がレンダ機能によって追跡されたときに実行されます。
- renderTriggered: リアクティブな依存関係の変更によってレンダリングがトリガーされたときに実行されます。
12. 'activated' と 'deactivated'
これらは、<KeepAlive> タグによってキャッシュされたダイナミックコンポーネントが切り替えられた際に使用されます。
- activated: キャッシュされたコンポーネントが「アクティブ」になったときに呼ばれます(マウント時も含む)。
- deactivated: コンポーネントが DOM から削除されず、キャッシュされた状態で「非アクティブ」になったときに呼ばれます。
12.1 実装例
<KeepAlive> を使用している場合、2回目以降の表示では mounted は実行されませんが、activated は毎回実行されます。
CompOne.vue:
activated() {
console.log("コンポーネントがアクティブになりました(キャッシュからの復帰を含む)");
},
deactivated() {
console.log("コンポーネントが非アクティブになりました(キャッシュへの保存)");
}