🔄

vue.jsのライフサイクルフックについて調べてみた

に公開

はじめに

入社3ヶ月目でVue.jsを使った開発案件にアサインされて、少しずつ業務に慣れてきたものの、最近特に「ライフサイクルフック」に触れる機会が増えてきました。
例えば、
・コンポーネントがマウントされた直後にAPIからデータを取得
・画面遷移の前後でイベントリスナーを登録解除
といった実装の中で、「どのタイミングで何をすべきか?」を意識する必要がある場面が多くなってきました。
しかし、正直なところまだライフサイクルフックをなんとなく使っていたため、今回Vueにおけるライフサイクルフックの中でよく使うものを体系的に整理し、理解を深めるためにこの記事を書くことにしました。

ライフサイクルフックとは

Vueコンポーネントは、
作成→DOMにマウント→更新→破棄
のようなライフサイクル(状態の変化)をたどり、このライフサイクルの各段階でフックを使うことで、任意の処理を差し込むことができます。
今回、以下の記事がわかりやすく解説されていたので参考にさせていただきます。
https://reisuta.com/lifecycle-hook/

Vueのライフサイクルフック

普段あまり見かけないものもありますが、以下のような種類があるようです。

主なライフサイクルフック一覧

タイミング Options API Composition API
作成前 beforeCreate() なし(setup前)
作成後 created() なし(setupで完了)
マウント前 beforeMount() onBeforeMount()
マウント後 mounted() onMounted()
更新前 beforeUpdate() onBeforeUpdate()
更新後 updated() onUpdated()
アンマウント前 beforeUnmount() onBeforeUnmount()
アンマウント後 unmounted() onUnmounted()
エラーキャッチ errorCaptured() onErrorCaptured()

ここでOptions API、Composition APIというワードが出てきたので、次で簡単にまとめたいと思います。

Composition API と Options API

両者の違いについては、こちらの記事に詳しい解説が書かれています。
https://techmania.jp/blog/vue-js-composition-api/

Composition API

Composition APIとは、Vue3から導入された新機能で、コンポーネントを実装するAPI。
リアクティブな値や関連する関数をコンポーネントから切り離して扱えるという特徴を持ち、コードの再利用性や保守性の向上が期待されています。

Options API

Options APIは、Vue2から使用されていた機能で、dataやmethodなどの各オプションごとにロジックを定義していく記法が特徴です。

両者の比較

Options APIは、リアクティブな値をviewから切り離せず、ロジックを分離できない問題がありますが、Composition APIでは、viewとロジックを分離することが可能で、可読性も向上します。

よく使うフックの役割と使い方

今回は、普段業務で使用している、Composition APIを中心に、よく使っているフックを詳しく見ていこうと思います。

onMounted

コンポーネントがDOMに挿入された直後(画面に表示された直後)に実行されます。
主な用途としては、
・初期データの取得(API通信)
・DOM要素へのアクセス
・イベントリスナーの登録
・アニメーション開始
などがあります。

使用例

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

onMounted(() => {
  console.log('コンポーネントがマウントされました')

  // 例:APIから初期データを取得
  fetch('/api/items')
    .then(response => response.json())
    .then(data => {
      console.log('データ取得完了', data)
    })
})
</script>

実行結果
コンポーネントがマウントされました
データ取得完了

onUnmounted

コンポーネントがDOMから削除される直前(画面から消える直前)に実行されます。
主な用途としては、
・タイマーやイベントリスナーの解除
・メモリリークの防止
・クリーンアップ処理
などがあります。

使用例

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

function handleResize() {
  console.log('リサイズされました')
}

onMounted(() => {
  window.addEventListener('resize', handleResize)
  console.log('リサイズイベント登録')
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
  console.log('リサイズイベント解除')
})
</script>

実行結果
1.画面表示時
リサイズイベント登録
2.リサイズイベント実行
リサイズされました
3.コンポーネントが非表示・削除されたとき
リサイズイベント解除

onUpdated

コンポーネントの状態(データやpropsなど)が変わり、DOMが更新された直後に実行されます。
主な用途としては、
・DOMの変更後に何か処理したいとき
・デバッグログの出力
・フォーカスやスクロール制御
などがあります。

使用例

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

const count = ref(0)

onUpdated(() => {
  console.log(`コンポーネントが更新されました。現在のcount: ${count.value}`)
})
</script>

<template>
  <button @click="count++">カウント:{{ count }}</button>
</template>

実行結果
1.初回は出力なし
2.ボタンをクリック(count++)するたびに出力
コンポーネントが更新されました。現在のcount: 1
コンポーネントが更新されました。現在のcount: 2
コンポーネントが更新されました。現在のcount: 3

実用例

業務で使用した例です。
onMountedとonUnmountedを使ったキーボード入力イベントリスナーの登録・解除

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

const handleKeypress = (e) => {
  if (e.key === "任意のキーボード入力") {
    // キー入力時に行いたい処理
  }
}

onMounted(() => {
  document.addEventListener("keydown", handleKeypress)
})

onUnmounted(() => {
  document.removeEventListener("keydown", handleKeypress)
})
</script>

キー入力時に何か処理を行いたいときに使用します。
コンポーネントがマウントされた直後にイベントリスナーを登録し、
コンポーネントがアンマウントされる直前にイベントリスナーを削除しています。
コンポーネントが破棄された後にイベントリスナーが残らないようにします。
メモリリークや、予期しない動作を防ぐことができます。

最後に

今回は案件で使用経験のあるものが中心となりましたが、冒頭で述べたように他にも様々なライフサイクルフックがあり、開発内容によってはこれから使う機会もあるかと思います。
適切なフックを使うことができれば、バグの削減、可読性・保守性の向上、パフォーマンス最適化などたくさんのメリットがあるようなので、今後の開発でライフサイクルフックを適切に使えるようになりたいです。

株式会社アクトビ

Discussion