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

# はじめに（Introduction）

Vueのコードを見る機会がありまして、初期処理を`created`と`mounted`で行うパターンがあり、どういう状態なのかを調べてみました。

Vueはバージョン3を使用します。（バージョンが異なると名称が異なります。）

# ライフサイクルフック（Lifecycle Hooks）

[ライフサイクルフック](https://v3.ja.vuejs.org/guide/instance.html#%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB%E3%83%95%E3%83%83%E3%82%AF
)を見てみます。

図（ https://v3.ja.vuejs.org/images/lifecycle.svg ）には以下８個のフックが記載されています。

`beforeCreate`
`created`
`beforeMount`
`mounted`
`beforeUpdate`
`updated`
`beforeUnmount`
`unmounted`

# サンプルコード（Sample Code）

各フックにログを入れて状態を確認します。
コードは以下となります、また[Github](https://github.com/tnakagawa/education/tree/master/vue3)にもあります。

<details><summary>コード</summary><div>

```html:index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>VUE 3 - Life Cycle</title>
    <link rel="stylesheet" type="text/css" href="style.css">
    <script src="https://unpkg.com/vue@next"></script>
    <script src="script.js"></script>
</head>

<body>
    <div id="counter">
        <div id="cnt" v-text="counter"></div>
    </div>
</body>

</html>
```

```javascript:script.js
'use strict';

var app = null;

function init() {
    console.log('Vue.version', Vue.version);
    app = Vue.createApp(LifeCycle);
    let vm = app.mount('#counter');
    console.log(app);
    console.log(vm);
}

const LifeCycle = {
    data() {
        return {
            counter: 0,
            intervalID: null,
        }
    },
    beforeCreate() {
        console.log('>>> beforeCreate');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeCreate');
    },
    created() {
        console.log('>>> created');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< created');
    },
    beforeMount() {
        console.log('>>> beforeMount');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeMount');
    },
    mounted() {
        console.log('>>> mounted');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< mounted');
        this.intervalID = setInterval(() => {
            console.log('count');
            this.counter++;
            if (this.counter > 2) {
                app.unmount();
            }
        }, 1000)
    },
    beforeUpdate() {
        console.log('>>> beforeUpdate');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt').innerText);
        console.log('<<< beforeUpdate');
    },
    updated() {
        console.log('>>> updated');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt').innerText);
        console.log('<<< updated');
    },
    beforeUnmount() {
        console.log('>>> beforeUnmount');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeUnmount');
    },
    unmounted() {
        console.log('>>> unmounted');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< unmounted');
        if (this.intervalID) {
            clearInterval(this.intervalID);
        }
    },
}

window.addEventListener('load', init);
```

</div></details>

# 実行結果（Execution Result）

各ライフライクルでどういう変化が起こっているのかを観察します。

## beforeCreate & created

```javascript
    beforeCreate() {
        console.log('>>> beforeCreate');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeCreate');
    },
    created() {
        console.log('>>> created');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< created');
    },
```

```text
>>> beforeCreate
undefined
null
null
<<< beforeCreate
>>> created
0
null
null
<<< created
```

`this.counter` が `undefined` から `0` に変更されています。
自身に`data`が適用されたようです。
DOMはまだ作成されていません。

## beforeMount & mounted

```javascript
    beforeMount() {
        console.log('>>> beforeMount');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeMount');
    },
    mounted() {
        console.log('>>> mounted');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< mounted');
        this.intervalID = setInterval(() => {
            console.log('count');
            this.counter++;
            if (this.counter > 2) {
                app.unmount();
            }
        }, 1000)
    },
```

```text
>>> beforeMount
0
null
null
<<< beforeMount
>>> mounted
0
<div id=​"cnt">​0​</div>​
<div id=​"cnt">​0​</div>​
<<< mounted
```

`this.$el` に テンプレートが適用されています。
`mounted`で1秒毎にカウントを加算するインターバルを設定します。

## beforeUpdate & updated

`mounted`で設定したインターバルでカウントが加算されると、更新のイベントが発生します。

```javascript
    beforeUpdate() {
        console.log('>>> beforeUpdate');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt').innerText);
        console.log('<<< beforeUpdate');
    },
    updated() {
        console.log('>>> updated');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt').innerText);
        console.log('<<< updated');
    },
```

```text
count
>>> beforeUpdate
1
<div id=​"cnt">​1​</div>​
0
<<< beforeUpdate
>>> updated
1
<div id=​"cnt">​1​</div>​
1
<<< updated
count
>>> beforeUpdate
2
<div id=​"cnt">​2​</div>​
1
<<< beforeUpdate
>>> updated
2
<div id=​"cnt">​2​</div>​
2
<<< updated
```

カウントが加算されると、`beforeUpdate`、`updated`とイベントが発生します。
`this.counter` が加算されており、`this.$el`も更新されている状態となります。
しかし、HTMLの値は、`beforeUpdate`ではカウントの加算前、`update`で加算後の値となっています。
ちなみにDOMは`this.$el`と同じでした。

## beforeUnmount & unmounted

`mounted`で設定したインターバルはカウントが2を超えると、`app.unmount();`を行い`unmount`処理が始まります。

```javascript
    beforeUnmount() {
        console.log('>>> beforeUnmount');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< beforeUnmount');
    },
    unmounted() {
        console.log('>>> unmounted');
        console.log(this.counter);
        console.log(this.$el);
        console.log(document.getElementById('cnt'));
        console.log('<<< unmounted');
        if (this.intervalID) {
            clearInterval(this.intervalID);
        }
    },
```

```text
count
>>> beforeUnmount
3
<div id=​"cnt">​2​</div>​
<div id=​"cnt">​2​</div>​
<<< beforeUnmount
>>> unmounted
3
<div id=​"cnt">​2​</div>​
null
<<< unmounted
```

`beforeUnmount`ではカウントは加算後の値、`this.$el`とDOMは更新されていない状態となります。
`unmounted`でDOMが削除されています。

## コンソールログ

<details><summary>Console log 全文</summary><div>

```text
Navigated to file:///C:/Users/marba/Desktop/work/education/vue3/index.html
vue@next:10907 You are running a development build of Vue.
Make sure to use the production build (*.prod.js) when deploying for production.
script.js:6 Vue.version 3.2.4
script.js:21 >>> beforeCreate
script.js:22 undefined
script.js:23 null
script.js:24 null
script.js:25 <<< beforeCreate
script.js:28 >>> created
script.js:29 0
script.js:30 null
script.js:31 null
script.js:32 <<< created
script.js:35 >>> beforeMount
script.js:36 0
script.js:37 null
script.js:38 null
script.js:39 <<< beforeMount
script.js:42 >>> mounted
script.js:43 0
script.js:44 <div id=​"cnt">​0​</div>​
script.js:45 <div id=​"cnt">​0​</div>​
script.js:46 <<< mounted
script.js:9 {_uid: 0, _component: {…}, _props: null, _container: div#counter, _context: {…}, …}
script.js:10 Proxy {…}
script.js:48 count
script.js:56 >>> beforeUpdate
script.js:57 1
script.js:58 <div id=​"cnt">​1​</div>​
script.js:59 0
script.js:60 <<< beforeUpdate
script.js:63 >>> updated
script.js:64 1
script.js:65 <div id=​"cnt">​1​</div>​
script.js:66 1
script.js:67 <<< updated
script.js:48 count
script.js:56 >>> beforeUpdate
script.js:57 2
script.js:58 <div id=​"cnt">​2​</div>​
script.js:59 1
script.js:60 <<< beforeUpdate
script.js:63 >>> updated
script.js:64 2
script.js:65 <div id=​"cnt">​2​</div>​
script.js:66 2
script.js:67 <<< updated
script.js:48 count
script.js:70 >>> beforeUnmount
script.js:71 3
script.js:72 <div id=​"cnt">​2​</div>​
script.js:73 <div id=​"cnt">​2​</div>​
script.js:74 <<< beforeUnmount
script.js:77 >>> unmounted
script.js:78 3
script.js:79 <div id=​"cnt">​2​</div>​
script.js:80 null
script.js:81 <<< unmounted
```

</div></details>


# まとめ（Conlution）

重要なのは、`created`、`mounted`、`unmounted`のようです。
用途としては以下が考えられると思いました。
`created`はHTML反映前にデータなどを更新する場合
`mounted`はHTML反映後に処理が必要な場合（定期的な処理など）
`unmounted`は後処理が必要な場合（後ろで動いている処理の停止など）