【Vue2 / Vue3】ライフサイクルフック

![ciclodevida-life-cycle.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2356280/214291ac-01e7-fa2f-f8ca-18d10dbcb849.gif)


## ライフサイクルフックとは
Vueコンポーネントには、以下のような**ライフサイクル**が存在します。

1. Vueインスタンスが作成される：**create**
1. VueインスタンスがDOM要素にマウント（`<template>`内がHTMLに変換）される：**mount**
1. リアクティブなデータが変更される：**update**
1. Vueインスタンスが破棄される：**destroy** / **unmount**

これらそれぞれのタイミングの**前後**で自動的に呼び出される処理を定義することができるメソッドが、Vueでは用意されています。これを「**ライフサイクルフック**」と呼びます。

## `beforeCreate`
**`data`などの初期化前、何よりも最初に実行される処理を記載するフックです。**

この時点では`data`や`methods`などの初期化は完了していないので、リアクティブなデータやイベントリスナーなどを利用（`data`の値を変更したり、参照したり）できません。

その為、`data`が関わらない（リアクティブ性が必要ない）処理を一番最初に行いたい際に有効です。

認証チェックのAPIなどをここで実行するのはありかなあとは思いますが、あまり`beforeCreate`を使用しているケースを見たことがありません。

### Vue2
~~~vue
<script>
export default {
  data () {
    return {
      name: "",
    };
  },
  beforeCreate() {
    console.log("Before create");
  },
};
</script>
~~~
### Vue3
Vue2で`beforeCreate`、`created`の中で表現していた処理は、`setup`内にそのまま処理を記載するような形になりました。

最初に実行される処理としてわかりやすいように、初期表示処理を即時関数として定義することをオススメします。
~~~vue
<script setup lang="ts">
import { ref } from "vue";

(() => {
  console.log("Before create");
})();

const name = ref<string>("");
</script>
~~~

## `created`
**`data`や`methods`の初期化は完了しているが、DOM（`<template>`の中）は生成されていない状態で実行されるフックです。**

`beforeCreated`内では`data`や`methods`の初期化が完了していなかった為アクセスすることができませんが、この時点からアクセスすることが可能です。

初期遷移時、DOMを生成する前に何かAPIを実行して、`data`の初期値として設定したりする場合は`created`で実行するのが良いとされています。

### Vue2
~~~vue
<script>
export default {
  data() {
    name: ""
  },
  created() {
    this.name = "Whopper";
  },
};
</script>
~~~

### Vue3
`beforeCreate`同様、Vue2まで`created`の中で表現していた処理は`setup`内にそのまま記載します。
~~~vue
<script setup lang="ts">
import { ref } from "vue";

const name = ref<string>("");

(() => {
  name.value = "Whopper";
})();
</script>
~~~



## `beforeMount`
**VueインスタンスがDOMにマウントされる前に実行されるフックです。**

>「VueインスタンスがDOMにマウントされる」というのは、「Vueの`<template>`内がコンパイルされ、実際のHTMLに変換されること」を言います。

`created`同様、`data`や`methods`の初期化は完了しているが、DOMにマウントされていない状態の為、違いがよく分からなくなりそうですが、実行されるタイミングという観点で言うと、だいたい同じものという認識で大丈夫な気もします。

### Vue2
~~~vue
<script>
export default {
  beforeMount() {
    console.log("before mount");
  },
};
</script>
~~~

### Vue3
~~~vue
<script setup lang="ts">
import { onBeforeMount } from "vue";

onBeforeMount(() => {
  console.log("before mount");
});
</script>
~~~


## `mounted`
**インスタンスがDOMにマウントされた後に実行されるフックです。**

DOM要素（`<template>`内）へのアクセスを伴う処理、外部ライブラリの初期化やイベントリスナーの設定などをする際に有効です。

使用している子コンポーネントが全てマウントされている & 現コンポーネントがマウントされたら初めて「マウントされた」ということになります。その為、子コンポーネント内の`mounted`は親コンポーネントの`mounted`より先に実行されるということに注意です。

### Vue2
~~~vue
<script>
export default {
  mounted() {
    console.log("mounted");
  },
};
</script>
~~~

### Vue3
~~~vue
<script setup lang="ts">
import { onMounted } from "vue";

onMounted(() => {
  console.log("mounted");
});
</script>
~~~

## 余談: `created`と`mounted`について
よく違いが曖昧になりがちな`created`と`mounted`ですが、

「**DOMへのマウント（`<template>`内HTMLへの変換）が完了しているかどうか**」という違いの他にも、「**`mounted`はSSR（サーバーサイドレンダリング）有効時には呼び出されない**」という点を覚えておくと尚良いです！

これは、`mounted`はあくまで**ブラウザ上で**VueインスタンスがDOMにマウントされたというイベントに対するフックであることが理由です。

SSRではVue.jsのコードは事前にサーバー上で実行され、レンダリングされたHTML（既に`<template>`内が変換されたもの）がブラウザに渡される為、「ブラウザ上でVueインスタンスがDOMにマウントされる」というイベントは発生しない為、`mounted`は実行されないのです。（`beforeMount`も同様です）

SSR有効時には、基本的には`created`を使用することが推奨されています。

## `beforeUpdate`
**`data`の値が更新され、DOMに適用される前に実行されるフックです。**

この時点ではDOMには反映されていないので、データのバリデーションや変更のキャンセルなど、更新前の操作を行うのに適しています。

たとえばフォームに入力された文字数が制限を超えた場合、新しいデータがDOMに反映される前に、文字数を制限内に調整するなどの場合はここに処理を書くのが相応しいです。

### Vue2
~~~vue
<script>
export default {
  beforeUpdate() {
    console.log("before update");
  },
};
</script>
~~~

### Vue3
~~~vue
<script setup lang="ts">
import { onBeforeUpdate } from "vue";

onBeforeUpdate(() => {
  console.log("before update");
});
</script>
~~~


## `updated`

**`data`の値が更新され、DOMに適用された後に実行されるフックです。**

DOMが更新された後の後処理や、更新されたDOM要素にアクセスして特定の処理を行うことができます。

たとえば、なんらかのリストにデータを追加したら画面上部までスクロールするなどの処理を実装する際に使えそうです。

### Vue2
~~~vue
<script>
export default {
  updated() {
    console.log("updated");
  },
};
</script>
~~~

> `beforeUpdate`と`updated`内で同じ`data`に対して更新をかけると無限ループに入るでご注意！

> 特定の`data`の変更を検知してなんらかの処理を実行したい場合は、Vueの[ウォッチャー（`watch`）](https://ja.vuejs.org/guide/essentials/watchers.html)を使用することを推奨します。

### Vue3
~~~vue
<script setup lang="ts">
import { onUpdated } from "vue";

onUpdated(() => {
  console.log("updated");
});
</script>
~~~

## `beforeDestroy` / `beforeUnmount`

**Vueインスタンスが破壊される前に実行されるフックです。**

Vue2では`beforeDestroy`、Vue3では`beforeUnmount`というメソッド名になります。

リソースの解放やイベントリスナーのクリーンアップ（メモリリークを防ぐ為に`addEventListener`で設定したイベントを`removeEventListener`で破棄する）など、インスタンスのクリーンアップに関連する処理を記載する場所として適しています。

### Vue2
~~~vue
<script>
export default {
  beforeDestroy() {
    console.error("before destroy");
  },
};
</script>
~~~

### Vue3
~~~vue
<script setup lang="ts">
import { onBeforeUnmount } from "vue";

onBeforeUnmount(() => {
  console.log("before unmount")
});
</script>
~~~

## `destroyed` / `unmounted`

**Vueインスタンスが破壊された後に実行されるフックです。**

Vue2では`destroyed`、Vue3では`unmounted`というメソッド名になります。

コンポーネントが完全にアンマウントされた後の後処理やリソース解放などを行うのに適しています。

### Vue2
~~~vue
<script>
export default {
  destroyed() {
    console.error("destroyed");
  },
};
</script>
~~~

### Vue3
~~~vue
<script setup lang="ts">
import { onUnmounted } from "vue";

onUnmounted(() => {
  console.log("unmounted")
});
</script>
~~~

## まとめ

| ライフサイクルフック名 | 実行タイミング |
| ---- | ---- |
| `beforeCreate` | `data`、`methods`等の初期化前 |
| `created` | `data`、`methods`等の初期化後 |
| `beforeMount` | `<template>`がHTMLに変換される前 |
| `mounted` | `<template>`がHTMLに変換された後 |
| `beforeUpdate` | `data`が更新され、DOMに反映される前 |
| `updated` | `data`が更新され、DOMに反映された後 |
| `beforeDestroy` / `beforeUnmount` | Vueインスタンスを破棄する前 |
| `destroyed` / `unmounted` | Vueインスタンスを破棄した後 |