はい、承知いたしました。Vue.jsのmounted
ライフサイクルフックに焦点を当て、その詳細、他のフックとの関連、使い方、注意点などを網羅した約5000語の記事を作成します。
Vue mounted をマスター!ライフサイクルフック徹底ガイド
はじめに:Vue.jsコンポーネントの「生涯」を理解する重要性
Vue.jsは、コンポーネント指向のモダンなJavaScriptフレームワークです。アプリケーションは、独立した再利用可能なコンポーネントの集まりとして構築されます。これらのコンポーネントは、単にテンプレートを表示するだけでなく、データの管理、ユーザーとのインタラクション、他のコンポーネントとの連携など、様々な役割を担います。
しかし、コンポーネントは静的な存在ではありません。作成され、画面に描画され、データが更新され、そして最終的には破棄されるという「生涯(ライフサイクル)」を持っています。このコンポーネントのライフサイクルの各段階で、Vueは特定のタイミングでコードを実行するための特別な関数を提供しています。これが「ライフサイクルフック」です。
ライフサイクルフックを理解し、適切に利用することは、Vue.js開発において非常に重要です。
- 正確なタイミングでの処理実行: コンポーネントの状態(データが準備できているか、DOMが利用可能かなど)に基づいて、コードを適切なタイミングで実行できます。
- リソース管理: コンポーネントが不要になったときに、設定したイベントリスナーやタイマー、外部ライブラリのインスタンスなどを適切に破棄し、メモリリークを防ぐことができます。
- 外部ライブラリとの連携: DOM要素に依存するライブラリ(チャート描画、リッチテキストエディタなど)を、DOMが利用可能になった後に初期化できます。
- コンポーネントの振る舞いの制御: コンポーネントの生成、更新、破棄といった様々なイベントに対して、カスタムロジックを組み込むことができます。
この記事では、Vue.jsの主要なライフサイクルフックの全体像を把握しつつ、特にアプリケーション開発で頻繁に利用されるmounted
フックに焦点を当てて、その機能、使い方、典型的なユースケース、注意点などを徹底的に解説します。mounted
フックをマスターすることで、Vue.jsコンポーネントのより高度な制御と、堅牢なアプリケーション開発が可能になります。
さあ、Vue.jsコンポーネントのライフサイクルの旅に出て、mounted
フックの秘密を解き明かしましょう。
第1章:Vue.jsコンポーネントのライフサイクルとは?
Vue.jsコンポーネントは、その誕生から消滅までの間に、いくつかの決まった段階を経由します。この一連の流れを「コンポーネントのライフサイクル」と呼びます。ライフサイクルは、主に以下の4つの主要なフェーズに分けられます。
- 生成(Creation)フェーズ:
- コンポーネントインスタンスが生成される段階です。
- データやイベント設定が初期化されますが、まだDOMにはマウントされていません。
- マウント(Mounting)フェーズ:
- コンポーネントのテンプレートがレンダリングされ、仮想DOMが構築され、最終的に実際のDOMに挿入される段階です。
- このフェーズが完了すると、コンポーネントはブラウザの画面上に表示され、操作可能な状態になります。
- 更新(Updating)フェーズ:
- コンポーネントで使用されているリアクティブデータ(
data
,props
など)が変更されたときに発生します。 - 変更に応じてコンポーネントが再レンダリングされ、仮想DOMが更新され、必要に応じて実際のDOMも更新されます。
- コンポーネントで使用されているリアクティブデータ(
- 破棄(Unmounting)フェーズ:
- コンポーネントが画面から削除される段階です。
- 親コンポーネントからの削除、ルーターによるビューの切り替えなどによって発生します。コンポーネントインスタンスは破棄され、関連するリソースが解放されます。
これらの各フェーズにおいて、Vueは開発者がカスタムコードを挿入するための特定のタイミングを提供しています。これがライフサイクルフックです。
第2章:ライフサイクルフックの全体像
Vue.jsは、前述の各フェーズにおいて、以下の標準的なライフサイクルフックを提供しています(Vue 3の場合)。これらのフックは、Options APIではコンポーネントオプションとして、Composition APIの<script setup>
ではonXxx
という形で利用できます。
ライフサイクルの流れに沿って、主要なフックを見ていきましょう。
生成(Creation)フェーズのフック
beforeCreate
:- いつ呼ばれるか: インスタンスが初期化された直後。
- この時点の状態: データやイベント(
data
,computed
,methods
,watch
,event listeners
)はまだ設定されていません。まだリアクティブではありません。 - 用途: ほとんど利用されません。インスタンスのプロパティにアクセスする前の最後のチャンスですが、多くの初期化は
created
で行う方が適切です。
created
:- いつ呼ばれるか: インスタンスが作成された後。
- この時点の状態: データやイベント(
data
,computed
,methods
,watch
,event listeners
)は設定され、リアクティブになっています。しかし、テンプレートのコンパイルやDOMへのマウントはまだ行われていません。this
キーワードでインスタンスのプロパティにアクセスできます。 - 用途: 初期データのフェッチ(API呼び出しなど)、リアクティブデータの初期設定。DOMへのアクセスが必要ないほとんどの初期化処理はここで行うのが適切です。サーバーサイドレンダリング(SSR)にも対応しています。
マウント(Mounting)フェーズのフック
beforeMount
:- いつ呼ばれるか: マウントが開始される前。テンプレートがコンパイルまたはレンダリングされた直後。
- この時点の状態: 仮想DOMツリーは作成されていますが、まだ実際のDOMには挿入(マウント)されていません。
this.$el
はまだ存在しないか、Vueが管理していないDOM要素を指しているかもしれません。 - 用途: 描画前の最後の変更を行う機会ですが、DOM操作はできません。ほとんど利用されません。
mounted
:- いつ呼ばれるか: コンポーネントがDOMにマウントされた後。
- この時点の状態: コンポーネントのテンプレートがレンダリングされ、実際のDOM要素としてドキュメント内に存在します。
this.$el
でコンポーネントのルートDOM要素にアクセスできます。子コンポーネントも全てマウントされています。(注: 親コンポーネントのmounted
は、そのすべての子コンポーネントのmounted
が実行された後に呼ばれます)。 - 用途: DOM要素へのアクセスや操作、DOMサイズに依存する処理、DOM要素を必要とする外部ライブラリの初期化、イベントリスナーの設定。本記事の主題です!
更新(Updating)フェーズのフック
beforeUpdate
:- いつ呼ばれるか: リアクティブデータの変更により、コンポーネントがDOMを更新しようとする前。
- この時点の状態: データは更新されていますが、DOMはまだ古い状態です。
- 用途: DOM更新前の状態にアクセスしたい場合。例えば、入力フィールドのカーソル位置を保存してからDOMが更新されるのを待つ、といった特定の高度なユースケースで使われることがあります。
updated
:- いつ呼ばれるか: リアクティブデータの変更により、コンポーネントのDOMが更新された後。
- この時点の状態: データもDOMも最新の状態です。
- 用途: DOM更新後の操作。例えば、DOMのサイズが変更された後にそれを取得する、といった処理。無限ループを引き起こさないように注意が必要です(このフック内でデータを変更すると再レンダリング ->
updated
再実行となる可能性があるため)。
破棄(Unmounting)フェーズのフック
beforeUnmount
:- いつ呼ばれるか: コンポーネントインスタンスが破棄される直前。
- この時点の状態: コンポーネントはまだ完全に機能しており、イベントリスナーなどは有効です。
- 用途: イベントリスナーの解除、タイマーのクリア、外部ライブラリインスタンスの破棄など、コンポーネントが消滅する前に必要なクリーンアップ処理を行うための最後の機会。
unmounted
:- いつ呼ばれるか: コンポーネントインスタンスが破棄された後。
- この時点の状態: コンポーネントはDOMから完全に削除され、リアクティブな接続も解除されています。
- 用途: コンポーネントに関連する全てのリソースが解放されたことを確認したい場合。
その他のフック
activated
:keep-alive
でキャッシュされているコンポーネントがアクティブになった(画面に表示された)ときに呼ばれます。
deactivated
:keep-alive
でキャッシュされているコンポーネントが非アクティブになった(画面から非表示になった)ときに呼ばれます。
errorCaptured
:- 子孫コンポーネントから発生したエラーを補足する際に使用します。
renderTracked
(開発環境のみ):- リアクティブな依存関係が追跡されたときに呼ばれます。デバッグ用途。
renderTriggered
(開発環境のみ):- リアクティブな依存関係によってレンダリングがトリガーされたときに呼ばれます。デバッグ用途。
これらのフックの中で、created
とmounted
は、コンポーネントの初期化段階で特に頻繁に使用されるフックです。created
はデータ初期化に、mounted
はDOM操作に特化しています。次に、mounted
フックに焦点を当てて深く掘り下げていきます。
第3章:mountedフックにフォーカス!その役割とタイミング
さあ、本記事の主役であるmounted
フックに迫ります。
mounted
フックの基本的な役割
mounted
フックは、Vue.jsコンポーネントがDOMにマウントされた直後に実行されるライフサイクルフックです。このフックの主な目的は、コンポーネントのDOM要素が実際にブラウザ上に存在し、アクセス可能になった状態を利用して、初期化や操作を行うことです。
Options APIではコンポーネントオプションとして定義します。
“`vue
“`
Composition APIの<script setup>
では、onMounted
という名前でインポートして使用します。
“`vue
“`
mounted
フックが呼ばれる正確なタイミング
mounted
フックは以下の状態が満たされた後に実行されることが保証されます。
- コンポーネントのテンプレートがレンダリングされ、仮想DOMツリーが構築された。
- 仮想DOMツリーに基づいて、実際のDOM要素が生成された。
- 生成された実際のDOM要素が、アプリケーションの親要素内(例えば
#app
要素など)に挿入(マウント)された。
このタイミングでは、コンポーネント自身のルート要素(this.$el
またはテンプレートのルート要素に対応するDOMノード)および、その子要素として描画されたVueによって生成されたDOM要素は全て利用可能な状態です。
重要な注意点:
- 子コンポーネントのmounted: 親コンポーネントの
mounted
フックは、全ての子コンポーネントのmounted
フックが実行された後に実行されます。これは、Vueがコンポーネントツリーをボトムアップでマウントしていくためです。親コンポーネントのmounted
が実行される時点では、その全ての子孫コンポーネントも既にDOMにマウントされ、初期化が完了している状態が保証されます。 - サーバーサイドレンダリング (SSR):
mounted
フックはサーバーサイドでは実行されません。SSRでは、サーバーはDOMを生成しますが、ブラウザのAPIやDOMの状態に依存するコードはサーバー上では実行できません。mounted
はブラウザ環境に依存するフックであるため、SSR時にはスキップされます。SSR対応のアプリケーションでDOMに依存しない初期化を行いたい場合は、created
フックを使用する必要があります。
mounted
で利用できるもの / できないもの
mounted
フックで利用できるもの:
- コンポーネントのリアクティブデータ (
this.myVariable
または Composition APIのref
/reactive
で定義した変数)。 - コンポーネントの算出プロパティ (
this.myComputedProperty
)。 - コンポーネントのメソッド (
this.myMethod()
)。 - コンポーネント自身のルートDOM要素 (
this.$el
またはref
で参照した要素)。 - テンプレート内で
ref
属性を付けて参照した任意のDOM要素または子コンポーネントインスタンス (this.$refs.myElement
または Composition APIのref
で参照した変数)。 - 親コンポーネントから渡されたprops (
this.myProp
). - Vueルーターのインスタンス (
this.$router
,this.$route
) など、グローバルプロパティやプラグイン。
mounted
フックで利用できない、または避けるべきこと:
- コンポーネントがDOMにマウントされる前のDOM操作: これは
mounted
フックの存在意義と矛盾します。beforeMount
などではDOMへのアクセスはできません。 - サーバーサイドレンダリングで実行する必要がある処理: SSR対応アプリケーションの場合、サーバー側で実行したい初期化(特にデータのフェッチ)は
created
などのフックで行うべきです。 - 無限ループを引き起こす可能性のある処理:
mounted
内でリアクティブデータを変更することは可能ですが、その変更がすぐにコンポーネントの再レンダリングをトリガーする場合、意図しない更新サイクルの連鎖を引き起こす可能性がないか注意が必要です。(ただし、mounted
内でデータを変更しても、通常は最初の描画後の非同期な更新キューに乗るため、直ちに無限ループになることは少ないですが、ロジックによっては注意が必要です。) - コンポーネントが破棄された後のクリーンアップを忘れること:
mounted
で設定したイベントリスナーやタイマーなどは、コンポーネントが破棄される際に手動で解除・クリアする必要があります。これを行わないとメモリリークの原因となります。クリーンアップは通常、beforeUnmount
フックで行います。
これらの点を踏まえると、mounted
フックは「コンポーネントが画面に表示され、操作可能になった後に行う、DOMに関連する初期化や外部連携」に最適な場所と言えます。
第4章:mountedフックの典型的なユースケース
mounted
フックは、以下のような様々なシナリオで強力なツールとなります。
1. DOM要素へのアクセスと操作
これはmounted
フックの最も基本的かつ一般的なユースケースです。コンポーネントのテンプレートがDOMにレンダリングされた後、特定の要素にアクセスして、そのサイズを取得したり、スタイルを操作したり、内容を変更したりする必要がある場合があります。
- 要素のサイズや位置の取得:
offsetHeight
,offsetWidth
,getBoundingClientRect()
などのDOM APIを使用して、要素の実際の描画サイズや位置を取得します。これは、例えば要素のサイズに応じて何かを計算したり、UIの配置を調整したりする場合に必要です。 - 特定の要素へのフォーカス設定: ページロード時に特定の入力フィールドやボタンにフォーカスを当てたい場合に利用します。
- Canvas要素への描画:
<canvas>
要素を取得し、描画コンテキスト(getContext('2d')
など)を取得して図形を描画するなどの処理を行います。
2. 外部ライブラリの初期化
Vue.jsコンポーネント内で、DOM要素に依存するサードパーティのJavaScriptライブラリを使用する場合、そのライブラリの初期化はmounted
フックで行うのが適切です。これらのライブラリは、多くの場合、初期化時に特定のDOM要素を引数として受け取り、その要素に対して様々な操作を行います。
例:
- チャート描画ライブラリ (Chart.js, D3.js): 特定の
<canvas>
や<div>
要素を取得し、そこにチャートを描画します。 - リッチテキストエディタ (TinyMCE, CKEditor):
<textarea>
要素などを取得し、それをリッチテキストエディタに変換します。 - スクロールライブラリ (Swiper, perfect-scrollbar): 特定のコンテナ要素に対してスクロール機能を適用します。
- DOM操作ライブラリ (jQuery – Vueでは非推奨ですが概念として): 特定の要素を選択して操作を行います。
これらのライブラリは、対象となるDOM要素が存在しないと正しく初期化できません。そのため、DOMが完全に準備された状態であるmounted
が最適な初期化場所となります。
3. 非同期処理(DOM準備後に必要な場合)
ほとんどのデータフェッチはDOMの準備を待つ必要がないため、created
フックで行うのが推奨されます。しかし、取得したデータを表示するためにDOMのサイズが必要な場合や、データのフェッチ自体がDOM要素への操作(例えば、特定の要素がクリックされたらデータをフェッチするなど)に依存する場合は、mounted
フックで非同期処理を開始することが考えられます。
例:
- 要素のサイズを取得し、そのサイズに合わせて画像をリサイズしてから表示するために、画像データをフェッチする。
- DOMに描画された要素に対してJavaScriptアニメーションを実行する(アニメーションライブラリのセットアップなど)。
ただし、DOMに依存しないデータのフェッチは、コンポーネントがより早くデータを取得できるように、できる限りcreated
で行うことを心がけましょう。
4. イベントリスナーの設定
ブラウザのウィンドウ全体や特定のDOM要素(コンポーネント自身以外のもの)に対してイベントリスナー(scroll
, resize
, click
など)を設定する場合、mounted
フックで行うのが一般的です。
例:
- ウィンドウのスクロール位置を監視し、特定のスクロール量を超えたらヘッダーを固定表示する。
- ウィンドウのリサイズを監視し、コンポーネントのレイアウトを動的に変更する。
- ドキュメント全体のクリックを監視し、モーダルやドロップダウン以外の場所がクリックされたらそれを閉じる。
重要: mounted
で設定したイベントリスナーは、必ずbeforeUnmount
で解除する必要があります。解除しないと、コンポーネントがDOMから削除されてもリスナーが残り続け、存在しない要素への参照によるエラーやメモリリークの原因となります。
5. 描画後のデータのフェッチ(より高度なケース)
前述のように、データフェッチは通常created
で行いますが、以下のような特定のケースではmounted
が検討されることがあります。
- 無限スクロール:
mounted
で最初のデータセットをフェッチし、その後、ユーザーがスクロールしてページの下部に到達したことをmounted
で設定したスクロールイベントリスナーで検知し、追加データをフェッチする。 - DOMの状態に基づいたデータ要求: 例えば、コンポーネントが表示されているビューポートのサイズに応じて異なる解像度の画像データを要求する場合など、初回描画後のDOMの状態がデータのフェッチ内容に影響する場合。
ここでも繰り返しになりますが、DOMの状態に依存しないデータフェッチは、ユーザー体験の向上(データが早く取得できる)やSSR対応の観点から、created
フックで行うのが一般的に良いプラクティスです。
これらのユースケースは、mounted
フックの強力さを示しています。DOMが利用可能であるという状態は、多くのクライアントサイドJavaScriptの機能やライブラリにとって不可欠な前提条件となるためです。
第5章:mountedフックの実践的な使い方(コード例)
ここでは、いくつかの具体的なコード例を通して、mounted
フックの実際の使い方を見ていきましょう。Options APIとComposition API (<script setup>
) の両方で例を示します。
例1:シンプルなDOM要素へのアクセスとテキスト表示
最も基本的な例として、mounted
フック内でコンポーネントのルート要素にアクセスし、そのテキスト内容を変更してみます。
Options API
“`vue
初期テキスト
“`
Composition API (<script setup>
)
Composition APIでは、テンプレート内の特定の要素にアクセスするためにref
を使用します。ref
は、DOMがマウントされた後に、テンプレート内で対応する要素への参照を保持します。
“`vue
初期テキスト
“`
どちらの例でも、mounted
(またはonMounted
)の中で、コンポーネントが実際にDOMに表示されている状態を利用して、その要素の内容やスタイルを変更しています。
例2:外部ライブラリ(Chart.js)の初期化
DOM要素に依存する外部ライブラリとして、Chart.jsを使って簡単な棒グラフを描画する例です。Chart.jsは描画対象となるcanvas
要素を必要とします。
まず、Chart.jsをインストールします。
npm install chart.js
または yarn add chart.js
Options API
“`vue
Chart Example
“`
Composition API (<script setup>
)
“`vue
Chart Example (Composition API)
“`
この例では、mounted
(またはonMounted
)でcanvas
要素への参照を取得し、その上でChart.jsを初期化しています。そして、非常に重要なこととして、beforeUnmount
(またはonBeforeUnmount
)でChart.jsのインスタンスを破棄することで、リソースのリークを防いでいます。
例3:ウィンドウのイベントリスナー設定と解除
ウィンドウのリサイズ時に何か処理を行う必要のあるコンポーネントを考えます。例えば、要素の幅を再計算するなどです。
Options API
“`vue
Resize Listener Example
ウィンドウの幅: {{ windowWidth }}px
“`
Composition API (<script setup>
)
“`vue
Resize Listener Example (Composition API)
ウィンドウの幅: {{ windowWidth }}px
“`
この例では、mounted
(またはonMounted
)でグローバルなwindow
オブジェクトに対してイベントリスナーを設定しています。そして、beforeUnmount
(またはonBeforeUnmount
)でそのリスナーを適切に解除しています。このように、mounted
とbeforeUnmount
は、リソースの確保と解放というペアでよく使用されます。
これらの例からわかるように、mounted
フックは、コンポーネントがブラウザのDOMツリーに組み込まれた状態を利用して、さまざまな初期化処理や外部との連携を行うための重要な入り口となります。そして、beforeUnmount
フックとセットで使うことで、リソース管理を適切に行うことができます。
第6章:他のライフサイクルフックとの比較 – mountedの立ち位置
Vueのライフサイクルには複数のフックがあり、それぞれ異なるタイミングと役割を持っています。mounted
フックをより深く理解するためには、他の重要なフックとの比較が役立ちます。特にcreated
とbeforeMount
、そしてupdated
フックとの違いを見ていきましょう。
mounted
vs created
これはVue開発者が最も頻繁に比較検討するフックの組み合わせかもしれません。
フック | 実行タイミング | 状態 | 主な用途 | SSR互換性 |
---|---|---|---|---|
created |
インスタンスが作成された後、データやイベントが初期化され、リアクティブになった時点。 | data , computed , methods , watch などが利用可能。まだテンプレートのコンパイル、DOMへのマウントは行われていない。 this.$el は未定義。 |
初期データのフェッチ、リアクティブデータの初期設定。DOMに依存しない初期化全般。 | 対応 |
mounted |
コンポーネントがDOMにマウントされた後。 | data , computed , methods , watch などが利用可能。テンプレートがレンダリングされ、実際のDOMとして利用可能。 this.$el でDOM要素にアクセス可能。 |
DOM操作、DOMに依存する外部ライブラリの初期化、イベントリスナー設定など。DOMが必要な初期化。 | 非対応 |
使い分けのポイント:
- データのフェッチ: DOMの状態(サイズや位置など)に依存しない初期データのフェッチは、
created
フックで行うのが一般的です。これにより、コンポーネントはDOMが準備されるよりも早くデータ取得を開始できます。SSR時にも動作するため、SEOや初回の表示速度向上にも寄与します。 - DOM操作: コンポーネントのDOM要素にアクセスしたり、それを操作したりする必要がある場合は、
mounted
フックを使用します。例えば、描画された要素のサイズを取得したり、要素に対してJavaScriptのアニメーションを適用したりする場合などです。 - 外部ライブラリの初期化: DOM要素を初期化引数として必要とする外部ライブラリ(Chart.js, TinyMCEなど)のセットアップは、必ず
mounted
フックで行います。 - SSR: SSRを考慮する必要がある場合は、
mounted
フック内のコードはサーバーでは実行されないことを念頭に置く必要があります。サーバーとクライアントの両方で実行したい初期化ロジックはcreated
に置くか、適切な分岐(if (process.client)
など)を使用します。
結論として、特別な理由がない限り、データのフェッチなどDOMに依存しない初期化はcreated
で、DOM操作やDOM依存の初期化はmounted
で行うのが良いプラクティスです。
mounted
vs beforeMount
beforeMount
はmounted
の直前に実行されるフックです。
beforeMount
: 仮想DOMツリーは生成されているが、まだ実際のDOMに挿入されていない状態。this.$el
はまだ存在しないか、Vueが管理していない要素。mounted
: 仮想DOMツリーに基づき実際のDOMが生成され、ドキュメント内にマウントされた状態。this.$el
が実際のDOM要素を指す。
beforeMount
は、描画直前の仮想DOMツリーにはアクセスできますが、まだ実際のDOMはありません。そのため、DOM操作やサイズ取得などはできません。特殊なユースケースを除き、beforeMount
が使われることはあまりありません。ほとんどの初期化処理はcreated
かmounted
で行うことになります。
mounted
vs updated
mounted
はコンポーネントの初回マウント後に一度だけ呼ばれるフックです。一方、updated
はコンポーネントのデータが変更され、DOMが再レンダリングされるたびに呼ばれるフックです。
mounted
: 初回マウント後の一度きり。DOMが最初に利用可能になった時点。updated
: データ変更によるDOM更新の度に呼ばれる。DOMが最新の状態になった時点。
updated
フックは、データ変更後にDOMがどのように変化したかを確認したり、DOMの変更に基づいて追加の操作(例えば、リストの要素数が増えたらスクロール位置を調整するなど)を行ったりする場合に使用します。mounted
はコンポーネントの「誕生時」の処理、updated
は「成長・変化時」の処理と捉えることができます。
updated
フック内でリアクティブデータを変更すると、再び更新サイクルが始まり、無限ループに陥る可能性があるため、利用には特に注意が必要です。
ライフサイクルの流れにおけるmounted
の位置づけ
視覚的にライフサイクルの流れを把握すると、mounted
フックの立ち位置がより明確になります。
“`
-> beforeCreate
-> created (データ・イベント初期化完了, リアクティブ化)
-> beforeMount (仮想DOM生成完了)
-> レンダリング & DOMマウント
-> mounted (DOMアクセス可能)
-> データ変更 -> beforeUpdate (データ更新済み、DOMは古い状態)
-> レンダリング & DOM更新
-> updated (データ・DOM最新の状態)
(データ変更がある限り、beforeUpdate -> updated を繰り返す)
-> beforeUnmount (インスタンス破棄直前)
-> DOMから削除
-> unmounted (インスタンス破棄済み)
“`
この図からもわかるように、mounted
は「DOMが初めて画面に表示され、操作可能になった」という重要なマイルストーンを示すフックです。
第7章:mountedフック利用上の注意点とベストプラクティス
mounted
フックを効果的かつ安全に利用するためには、いくつかの注意点と推奨されるプラクティスがあります。
1. サーバーサイドレンダリング (SSR) への非対応
前述の通り、mounted
フックはブラウザ環境に依存するため、SSR時には実行されません。SSR対応のアプリケーションを開発している場合、この点は非常に重要です。
- データフェッチ: SSRでデータをプリフェッチしてHTMLに埋め込む場合、そのデータフェッチロジックは
created
(またはComposition APIのsetup
関数内や非同期コンポーネントの解決時など、ビルド設定に応じた場所)に記述する必要があります。mounted
でデータをフェッチすると、初回のSSRされたHTMLにはデータが含まれず、クライアント側でのハイドレーション後に初めてデータが表示されることになります。 - DOM依存ロジック: SSR環境ではDOMが存在しないため、
mounted
内に記述したDOM操作やDOMを必要とする外部ライブラリの初期化は当然実行されません。これは期待通りの挙動ですが、もしこれらの処理がアプリケーションの初回表示にとって必須である場合は、SSR時の代替手段を検討する必要があるかもしれません(例:CSSで初期表示を調整する、プレースホルダーを表示するなど)。 - 条件分岐: サーバーとクライアントで異なる処理を行いたい場合は、環境判定(
typeof window === 'undefined'
またはprocess.client
など)を使用してコードを分岐させることができます。ただし、Vueのライフサイクルフックを適切に使い分ける方が、よりクリーンなコードになります。
ベストプラクティス: SSR対応のアプリケーションでは、DOMが不要な初期化はcreated
に、DOMが必要な初期化はmounted
に、と明確に使い分ける習慣をつけましょう。
2. クリーンアップの徹底 (beforeUnmount
の活用)
mounted
フック内で設定したグローバルなイベントリスナー(window.addEventListener
など)、タイマー(setInterval
, setTimeout
)、または外部ライブラリのインスタンスなどは、コンポーネントが破棄される際に必ず手動でクリーンアップする必要があります。これを怠ると、メモリリークや、コンポーネントが既に存在しないにも関わらずコードが実行されてしまうといった問題を引き起こします。
クリーンアップは、mounted
と対になるbeforeUnmount
フックで行うのが標準的なプラクティスです。
“`vue
“`
Composition API (<script setup>
) の場合も同様に、onMounted
で設定したクリーンアップが必要な処理は、onBeforeUnmount
内で対応するクリーンアップコードを記述します。
“`vue
“`
クリーンアップは、アプリケーションの安定性とパフォーマンスを維持するために絶対に疎かにしてはならないステップです。
3. DOMに依存しない処理は避ける
DOMへのアクセスや操作が必要ない初期化処理(例:コンポーネント自身の状態に基づいたデータ計算、ストアの初期化、DOMに依存しないAPI呼び出しなど)は、mounted
ではなくcreated
フックで行うべきです。これにより、コンポーネントはDOMの準備を待たずに初期化を進めることができ、パフォーマンス向上やSSR互換性の恩恵を受けられます。
4. 重い処理は注意深く扱う
mounted
フック内で時間のかかる同期処理(例:巨大なデータの同期的な計算、複雑なDOM操作の繰り返しなど)を実行すると、ブラウザのメインスレッドがブロックされ、コンポーネントの描画完了後すぐにUIがフリーズしたように見えてしまう可能性があります。
もしmounted
内で重い処理が必要な場合は、以下のような対策を検討してください。
- 非同期化:
async/await
やsetTimeout
、requestAnimationFrame
などを利用して処理を分割し、ブラウザのレンダリングを妨げないようにします。 - Web Workerの利用: メインスレッドから分離して計算処理を行う必要がある場合は、Web Workerの利用を検討します。
- 処理の最適化: そもそも処理自体を効率化できないか検討します。
5. $nextTick
との連携
mounted
フック内ではコンポーネント自身のDOMは利用可能ですが、非同期的なDOM更新(例えば、v-if
やv-for
によって条件付きまたは繰り返しで描画される要素、データの変更によって内容が変わる要素など)の結果として生成されるDOM要素にアクセスしたい場合は、mounted
の時点ではまだそれらの要素がDOMに存在しない可能性があります。
VueのリアクティブシステムによるDOM更新は非同期に行われます。データ変更後、DOMが実際に更新されるのを待ってから処理を行いたい場合は、this.$nextTick()
(Options API)またはnextTick()
(Composition API)を利用します。
“`vue
{{ message }}
“`
mounted
フック自身は初回描画後に呼ばれますが、その中でさらにデータの変更を伴う場合は$nextTick
が必要になることがある点を理解しておきましょう。
6. 子コンポーネントのmountedタイミングを理解する
前述の通り、親コンポーネントのmounted
は、そのすべての子孫コンポーネントのmounted
が実行された後に呼ばれます。これは、Vueがコンポーネントツリーをボトムアップでマウントしていくためです。
つまり、親コンポーネントのmounted
フック内からは、そのテンプレート内に直接配置された子コンポーネントも、さらにその子コンポーネントも、全てDOMにマウントされ、初期化が完了した状態であると期待できます。これは、子コンポーネントが描画したDOM要素に親コンポーネントからアクセスしたい場合などに便利な挙動です。
第8章:mountedとリアクティブシステム
mounted
フックはコンポーネントの初期描画後に実行されますが、その内部でリアクティブデータを変更することは可能です。
mounted
内でリアクティブデータを変更すると、Vueのリアクティブシステムはその変更を検知し、コンポーネントの更新サイクル(beforeUpdate
-> updated
)をトリガーします。これは、mounted
フックが実行されるのは最初のマウント時の一度きりであるのに対し、データ更新による再描画は複数回発生する可能性があることを意味します。
例:
“`vue
Count: {{ count }}
“`
この例のコンソール出力順序は以下のようになるはずです(非同期キューの挙動に依存します)。
mounted: count is 0
mounted: count set to 1
beforeUpdate: count is 1
beforeUpdate: DOM count is Count: 0
updated: count is 1
updated: DOM count is Count: 1
mounted
内でデータを変更しても、その変更に対応するDOMの更新はすぐに同期的には行われず、Vueの更新キューを通じて非同期に処理されます。そのため、mounted
内でデータを変更した後、その変更が反映されたDOMにすぐにアクセスしたい場合は、this.$nextTick()
を使用する必要があるという点に注意が必要です。
mounted
内でリアクティブデータを変更すること自体は問題ありませんが、その変更が意図しない再レンダリングやパフォーマンスの問題を引き起こさないか、特に注意深くコードを記述する必要があります。ほとんどの場合、データの初期化はcreated
で行い、mounted
はDOMに特化した処理に集中させるのがシンプルで分かりやすい設計となります。
第9章:mountedの高度なトピック
より複雑なシナリオにおけるmounted
フックの挙動や関連する概念について触れておきましょう。
1. ネストされたコンポーネントとmountedの実行順序
繰り返しになりますが、Vueはコンポーネントツリーをボトムアップでマウントします。つまり、親コンポーネントよりも子コンポーネントの方が先にマウントされます。したがって、mounted
フックの実行順序は以下のようになります。
- 最も深い子コンポーネントの
mounted
- その親コンポーネントの
mounted
- さらにその親コンポーネントの
mounted
- …
- ルートコンポーネントの
mounted
この順序は、親コンポーネントのmounted
が実行される時点では、その全ての子孫コンポーネントが完全にDOMに描画され、初期化が完了していることを保証します。これにより、親コンポーネントは子コンポーネントの描画結果(例えば子コンポーネントのルート要素のサイズなど)に依存する処理を安心してmounted
内で行うことができます。
2. 動的コンポーネントとmounted
<component :is="currentComponent">
のような動的コンポーネントを使用する場合、表示されるコンポーネントが切り替わるたびに、新しいコンポーネントがマウントされ、古いコンポーネントがアンマウントされます。
- 新しいコンポーネントが表示されるとき: そのコンポーネントのライフサイクル(
beforeCreate
->created
->beforeMount
->mounted
)が実行されます。 - 古いコンポーネントが非表示になるとき: そのコンポーネントのライフサイクル(
beforeUnmount
->unmounted
)が実行されます。
したがって、動的に切り替わるコンポーネントの場合、mounted
フックはコンポーネントがアクティブになるたびに(つまり、<component>
タグに渡されたis
の値がそのコンポーネントを指すようになるたびに)実行されると考えることができます。
3. keep-alive
とactivated
/deactivated
<keep-alive>
コンポーネントは、動的コンポーネントやルータービューをラップすることで、非アクティブになったコンポーネントを破棄せずにキャッシュしておき、再度アクティブになったときに再利用する仕組みを提供します。
<keep-alive>
でラップされたコンポーネントは、初めて表示される際に通常のライフサイクル(beforeCreate
-> created
-> beforeMount
-> mounted
)を実行します。しかし、その後非アクティブになってもbeforeUnmount
やunmounted
は呼ばれず、代わりにdeactivated
フックが呼ばれます。
そして、再度アクティブになった際には、beforeCreate
からmounted
までのライフサイクルはスキップされ、代わりにactivated
フックが呼ばれます。
コンポーネントの状態 | 非keep-alive の場合のフック |
keep-alive の場合のフック |
---|---|---|
初回表示 | created , mounted |
created , mounted (activated も同時に呼ばれることに注意) |
非アクティブ化 | beforeUnmount , unmounted |
deactivated |
再アクティブ化 | (created , mounted が再実行) |
activated (データやDOM状態はキャッシュされたものが維持されるため、mounted のような効果を得る) |
つまり、<keep-alive>
でキャッシュされるコンポーネントにとって、activated
フックは非キャッシュの場合のmounted
フックに相当するタイミングと役割を担います。コンポーネントが「ビューに入った」時に行いたい処理(例:データの再フェッチ、イベントリスナーの再設定、スクロール位置の復元など)は、activated
フックに記述するのが適切です。mounted
フックは初回マウント時にのみ呼ばれます。
keep-alive
を利用する場合は、mounted
だけでなくactivated
とdeactivated
フックも理解しておく必要があります。
4. errorCaptured
フック
mounted
フック内を含め、コンポーネントのレンダリング中やライフサイクルフック内で発生したエラーは、親コンポーネントのerrorCaptured
フックで捕捉することができます。
“`vue
Something went wrong: {{ error }}
“`
この例のように、子コンポーネントのmounted
で発生したエラーは、親コンポーネントのerrorCaptured
で捕捉して、エラーUIを表示したり、ログを記録したりといった処理を行うことができます。これはエラーハンドリング戦略において役立ちます。
これらの高度なトピックを理解することで、mounted
フックが Vue.jsの他の機能やコンポーネントの振る舞いとどのように連携しているか、より全体像を掴むことができます。
第10章:まとめ – mountedをマスターするということ
この記事では、Vue.jsコンポーネントのライフサイクル全体を概観し、その中でmounted
フックがどのような位置づけにあるのか、そしてその機能と使い方について深く掘り下げてきました。
mounted
フックは、コンポーネントが仮想DOMから実際のDOMにレンダリングされ、ブラウザのドキュメント内に完全に挿入された直後に実行される、非常に重要なライフサイクルフックです。このタイミングは、DOM要素へのアクセスや操作、DOMを必要とする外部ライブラリの初期化、ブラウザのイベントリスナー設定など、多くのクライアントサイドJavaScriptの初期化処理にとって不可欠なものです。
mounted
フックをマスターするということは、単にその存在を知っているだけでなく、以下の点を深く理解し、実践できることを意味します。
- 正確な実行タイミング: コンポーネント自身のDOMだけでなく、その子孫コンポーネントのDOMも利用可能になった後に実行されることを理解する。
- 適切なユースケース: DOM操作、外部ライブラリ初期化、イベントリスナー設定など、
mounted
が最適なシナリオを判断できる。DOMに依存しない処理(特にデータのフェッチ)はcreated
で行うという使い分けの原則を理解する。 - SSRとの非互換性: サーバーサイドレンダリング環境では
mounted
が実行されないことを認識し、SSR対応アプリケーションでは代替手段や環境判定を適切に利用する。 - クリーンアップの重要性:
mounted
で確保したリソース(イベントリスナー、タイマー、ライブラリインスタンスなど)は、beforeUnmount
で必ず解放するという責任を果たす。 - 他のフックとの連携:
created
,beforeUnmount
,updated
,activated
/deactivated
といった他のライフサイクルフックの役割を理解し、mounted
と組み合わせてコンポーネントの振る舞いを正確に制御できる。 - リアクティブシステムとの関係:
mounted
内でデータを変更した場合の更新サイクルの挙動や、$nextTickの必要性を理解する。
Vue.jsコンポーネントは、その状態と描画が連携して動く「生き物」のようなものです。ライフサイクルフックは、その生命活動の節目節目で開発者が介入するための窓口を提供します。特にmounted
フックは、コンポーネントが「画面に姿を現す」という劇的な変化の瞬間を捉え、その後のインタラクションや動的な振る舞いを可能にするための基盤を築きます。
この記事が、あなたのVue.js開発においてmounted
フック、そしてライフサイクルフック全体をより深く理解し、自信を持って使いこなすための一助となれば幸いです。ライフサイクルを理解することは、Vue.jsの強力な機能を最大限に引き出し、より堅牢でパフォーマンスの高い、メンテナンスしやすいアプリケーションを構築するための鍵となるでしょう。
これからもVue.jsの学習と開発を楽しんでください!