Vue mounted をマスター!ライフサイクルフック徹底ガイド

はい、承知いたしました。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つの主要なフェーズに分けられます。

  1. 生成(Creation)フェーズ:
    • コンポーネントインスタンスが生成される段階です。
    • データやイベント設定が初期化されますが、まだDOMにはマウントされていません。
  2. マウント(Mounting)フェーズ:
    • コンポーネントのテンプレートがレンダリングされ、仮想DOMが構築され、最終的に実際のDOMに挿入される段階です。
    • このフェーズが完了すると、コンポーネントはブラウザの画面上に表示され、操作可能な状態になります。
  3. 更新(Updating)フェーズ:
    • コンポーネントで使用されているリアクティブデータ(data, propsなど)が変更されたときに発生します。
    • 変更に応じてコンポーネントが再レンダリングされ、仮想DOMが更新され、必要に応じて実際のDOMも更新されます。
  4. 破棄(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 (開発環境のみ):
    • リアクティブな依存関係によってレンダリングがトリガーされたときに呼ばれます。デバッグ用途。

これらのフックの中で、createdmountedは、コンポーネントの初期化段階で特に頻繁に使用されるフックです。createdはデータ初期化に、mountedはDOM操作に特化しています。次に、mountedフックに焦点を当てて深く掘り下げていきます。

第3章:mountedフックにフォーカス!その役割とタイミング

さあ、本記事の主役であるmountedフックに迫ります。

mountedフックの基本的な役割

mountedフックは、Vue.jsコンポーネントがDOMにマウントされた直後に実行されるライフサイクルフックです。このフックの主な目的は、コンポーネントのDOM要素が実際にブラウザ上に存在し、アクセス可能になった状態を利用して、初期化や操作を行うことです。

Options APIではコンポーネントオプションとして定義します。

“`vue


“`

Composition APIの<script setup>では、onMountedという名前でインポートして使用します。

“`vue


“`

mountedフックが呼ばれる正確なタイミング

mountedフックは以下の状態が満たされた後に実行されることが保証されます。

  1. コンポーネントのテンプレートがレンダリングされ、仮想DOMツリーが構築された。
  2. 仮想DOMツリーに基づいて、実際のDOM要素が生成された。
  3. 生成された実際の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

“`

Composition API (<script setup>)

“`vue

“`

この例では、mounted(またはonMounted)でcanvas要素への参照を取得し、その上でChart.jsを初期化しています。そして、非常に重要なこととして、beforeUnmount(またはonBeforeUnmount)でChart.jsのインスタンスを破棄することで、リソースのリークを防いでいます。

例3:ウィンドウのイベントリスナー設定と解除

ウィンドウのリサイズ時に何か処理を行う必要のあるコンポーネントを考えます。例えば、要素の幅を再計算するなどです。

Options API

“`vue

“`

Composition API (<script setup>)

“`vue

“`

この例では、mounted(またはonMounted)でグローバルなwindowオブジェクトに対してイベントリスナーを設定しています。そして、beforeUnmount(またはonBeforeUnmount)でそのリスナーを適切に解除しています。このように、mountedbeforeUnmountは、リソースの確保と解放というペアでよく使用されます。

これらの例からわかるように、mountedフックは、コンポーネントがブラウザのDOMツリーに組み込まれた状態を利用して、さまざまな初期化処理や外部との連携を行うための重要な入り口となります。そして、beforeUnmountフックとセットで使うことで、リソース管理を適切に行うことができます。

第6章:他のライフサイクルフックとの比較 – mountedの立ち位置

Vueのライフサイクルには複数のフックがあり、それぞれ異なるタイミングと役割を持っています。mountedフックをより深く理解するためには、他の重要なフックとの比較が役立ちます。特にcreatedbeforeMount、そして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

beforeMountmountedの直前に実行されるフックです。

  • beforeMount: 仮想DOMツリーは生成されているが、まだ実際のDOMに挿入されていない状態。this.$elはまだ存在しないか、Vueが管理していない要素。
  • mounted: 仮想DOMツリーに基づき実際のDOMが生成され、ドキュメント内にマウントされた状態。this.$elが実際のDOM要素を指す。

beforeMountは、描画直前の仮想DOMツリーにはアクセスできますが、まだ実際のDOMはありません。そのため、DOM操作やサイズ取得などはできません。特殊なユースケースを除き、beforeMountが使われることはあまりありません。ほとんどの初期化処理はcreatedmountedで行うことになります。

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/awaitsetTimeoutrequestAnimationFrameなどを利用して処理を分割し、ブラウザのレンダリングを妨げないようにします。
  • Web Workerの利用: メインスレッドから分離して計算処理を行う必要がある場合は、Web Workerの利用を検討します。
  • 処理の最適化: そもそも処理自体を効率化できないか検討します。

5. $nextTickとの連携

mountedフック内ではコンポーネント自身のDOMは利用可能ですが、非同期的なDOM更新(例えば、v-ifv-forによって条件付きまたは繰り返しで描画される要素、データの変更によって内容が変わる要素など)の結果として生成されるDOM要素にアクセスしたい場合は、mountedの時点ではまだそれらの要素がDOMに存在しない可能性があります。

VueのリアクティブシステムによるDOM更新は非同期に行われます。データ変更後、DOMが実際に更新されるのを待ってから処理を行いたい場合は、this.$nextTick()(Options API)またはnextTick()(Composition API)を利用します。

“`vue

“`

mountedフック自身は初回描画後に呼ばれますが、その中でさらにデータの変更を伴う場合は$nextTickが必要になることがある点を理解しておきましょう。

6. 子コンポーネントのmountedタイミングを理解する

前述の通り、親コンポーネントのmountedは、そのすべての子孫コンポーネントのmountedが実行された後に呼ばれます。これは、Vueがコンポーネントツリーをボトムアップでマウントしていくためです。

つまり、親コンポーネントのmountedフック内からは、そのテンプレート内に直接配置された子コンポーネントも、さらにその子コンポーネントも、全てDOMにマウントされ、初期化が完了した状態であると期待できます。これは、子コンポーネントが描画したDOM要素に親コンポーネントからアクセスしたい場合などに便利な挙動です。

第8章:mountedとリアクティブシステム

mountedフックはコンポーネントの初期描画後に実行されますが、その内部でリアクティブデータを変更することは可能です。

mounted内でリアクティブデータを変更すると、Vueのリアクティブシステムはその変更を検知し、コンポーネントの更新サイクル(beforeUpdate -> updated)をトリガーします。これは、mountedフックが実行されるのは最初のマウント時の一度きりであるのに対し、データ更新による再描画は複数回発生する可能性があることを意味します。

例:

“`vue

“`

この例のコンソール出力順序は以下のようになるはずです(非同期キューの挙動に依存します)。

  1. mounted: count is 0
  2. mounted: count set to 1
  3. beforeUpdate: count is 1
  4. beforeUpdate: DOM count is Count: 0
  5. updated: count is 1
  6. updated: DOM count is Count: 1

mounted内でデータを変更しても、その変更に対応するDOMの更新はすぐに同期的には行われず、Vueの更新キューを通じて非同期に処理されます。そのため、mounted内でデータを変更した後、その変更が反映されたDOMにすぐにアクセスしたい場合は、this.$nextTick()を使用する必要があるという点に注意が必要です。

mounted内でリアクティブデータを変更すること自体は問題ありませんが、その変更が意図しない再レンダリングやパフォーマンスの問題を引き起こさないか、特に注意深くコードを記述する必要があります。ほとんどの場合、データの初期化はcreatedで行い、mountedはDOMに特化した処理に集中させるのがシンプルで分かりやすい設計となります。

第9章:mountedの高度なトピック

より複雑なシナリオにおけるmountedフックの挙動や関連する概念について触れておきましょう。

1. ネストされたコンポーネントとmountedの実行順序

繰り返しになりますが、Vueはコンポーネントツリーをボトムアップでマウントします。つまり、親コンポーネントよりも子コンポーネントの方が先にマウントされます。したがって、mountedフックの実行順序は以下のようになります。

  1. 最も深い子コンポーネントのmounted
  2. その親コンポーネントのmounted
  3. さらにその親コンポーネントのmounted
  4. ルートコンポーネントのmounted

この順序は、親コンポーネントのmountedが実行される時点では、その全ての子孫コンポーネントが完全にDOMに描画され、初期化が完了していることを保証します。これにより、親コンポーネントは子コンポーネントの描画結果(例えば子コンポーネントのルート要素のサイズなど)に依存する処理を安心してmounted内で行うことができます。

2. 動的コンポーネントとmounted

<component :is="currentComponent">のような動的コンポーネントを使用する場合、表示されるコンポーネントが切り替わるたびに、新しいコンポーネントがマウントされ、古いコンポーネントがアンマウントされます。

  • 新しいコンポーネントが表示されるとき: そのコンポーネントのライフサイクル(beforeCreate -> created -> beforeMount -> mounted)が実行されます。
  • 古いコンポーネントが非表示になるとき: そのコンポーネントのライフサイクル(beforeUnmount -> unmounted)が実行されます。

したがって、動的に切り替わるコンポーネントの場合、mountedフックはコンポーネントがアクティブになるたびに(つまり、<component>タグに渡されたisの値がそのコンポーネントを指すようになるたびに)実行されると考えることができます。

3. keep-aliveactivated/deactivated

<keep-alive>コンポーネントは、動的コンポーネントやルータービューをラップすることで、非アクティブになったコンポーネントを破棄せずにキャッシュしておき、再度アクティブになったときに再利用する仕組みを提供します。

<keep-alive>でラップされたコンポーネントは、初めて表示される際に通常のライフサイクル(beforeCreate -> created -> beforeMount -> mounted)を実行します。しかし、その後非アクティブになってもbeforeUnmountunmountedは呼ばれず、代わりに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だけでなくactivateddeactivatedフックも理解しておく必要があります。

4. errorCapturedフック

mountedフック内を含め、コンポーネントのレンダリング中やライフサイクルフック内で発生したエラーは、親コンポーネントのerrorCapturedフックで捕捉することができます。

“`vue

“`

この例のように、子コンポーネントの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の学習と開発を楽しんでください!


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール