【Vue.js】動的にコンポーネントを切り替える際に、切り替え毎にdestroyedされないようにするには

## はじめに
仕事で使う事になったので1からVue.jsについて学んだ。
ちゃんと覚えておかないとまずそうな事を備忘録として1つ1つ残しておく。

## 動的にコンポーネントを切り替える際に、切り替え毎にdestroyedされないようにするには
### 動的なコンポーネントの切り替えについて
`<component></component>`タブと`v-bind:is=""（v-bindディレクティブのis属性）`を用いる事で以下の動画のように動的にコンポーネントを切り替える事ができる。
![ezgif.com-gif-maker (7).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/e0dce217-343a-3af2-f0ab-6db4e117a54c.gif)
動画のソースコードは以下。

```vue:App.vue
<template>
  <div>
    <!-- 省略 -->
    <button @click="currenComponent = 'Home'">Home</button>
    <button @click="currenComponent = 'About'">About</button>
    <component :is="currenComponent"></component>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
import About from "./components/About";
import Home from "./components/Home";

export default {
  // 省略
  components: {
    LikeHeader,
    About,
    Home,
  },
  // 省略
};
</script>

<style scoped>
/* 省略 */
</style>
```
```vue:About.vue
<template>
  <p>About</p>
</template>
```
```vue:Home.vue
<template>
  <p>Home</p>
</template>
```

ソースコード全体は以下。

https://github.com/yuta-katayama-23/intro-vuecli/tree/ee267c40c1073a9e71d09aa5fbae8fc0fe5f1b9f

### 動的なコンポーネント切り替えでは、切り替え毎にコンポーネントが`destroyed`される
Vue.jsの仕様として、コンポーネントを切り替えるとその度にコンポーネントが`destroyed`される。
![ezgif.com-gif-maker (8).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/3c997e59-6f4d-dc1b-7eff-146e87eb81b9.gif)
動画のソースコードは以下（`App.vue`は[上記](#動的なコンポーネントの切り替えについて)と同じ）。

```vue:About.vue
<template>
  <p>Home</p>
</template>

<script>
export default {
  destroyed() {
    console.log("Home destroyed");
  },
};
</script>
```
```vue:Home.vue
<template>
  <div>
    <p>About</p>
    <input type="text" />
  </div>
</template>

<script>
export default {
  destroyed() {
    console.log("About destroyed");
  },
};
</script>
```

ソースコード全体は以下。

https://github.com/yuta-katayama-23/intro-vuecli/tree/5fb9d9e8eebc59994e40f525ded70e86bf429a7b

コンポーネントを切り替えると自動的に前のコンポーネントは破棄されてなくなるので、いちいちdestroyedする必要がなくなり基本的には都合がいい。
ただ、この仕様で困るのは、
コンポーネントのデータは保持しておきたいといった場面で、タブ切り替えで動的にコンポーネントが切り替えた際に、コンポーネントのデータが消えてしまうという事。
これを防ぐ（動的にコンポーネントを切り替えても`destroyed`されないようにする）のに`<keep-alive></keep-alive>`タグを使う。

### `<keep-alive></keep-alive>`タグで囲む事で`destroyed`されないようにできる
`<<keep-alive></keep-alive>>`タグでラップ（囲む）だけでOK。
![ezgif.com-gif-maker (9).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/273ba625-76a5-f0ed-3cea-76a976e05b69.gif)
動画のソースコードは以下（`About.vue`・`Home.vue`は[上記](#動的なコンポーネント切り替えでは切り替え毎にコンポーネントがdestroyedされる)と同じ）。

```vue:App.vue
<template>
  <div>
    <!-- 省略 -->
    <button @click="currenComponent = 'Home'">Home</button>
    <button @click="currenComponent = 'About'">About</button>
    <keep-alive>
      <component :is="currenComponent"></component>
    </keep-alive>
  </div>
</template>

<script>
import LikeHeader from "./components/LikeHeader.vue";
import About from "./components/About";
import Home from "./components/Home";

export default {
  // 省略
  components: {
    LikeHeader,
    About,
    Home,
  },
  // 省略
};
</script>

<style scoped>
/* 省略 */
</style>
```

ソースコード全体は以下。

https://github.com/yuta-katayama-23/intro-vuecli/tree/245ebfc0fe7bba1ccbe08a2cc87a72f080fb1988

上記のようにする事で`destroyed`が実行されなくなるのでコンポーネントのデータも保持されるようになる。
ただこの時、通常のVueインスタンスのライフサイクルフックが使えないが、この時は`activated・deactivated`を使う。

### `<keep-alive></keep-alive>`を使っている時のライフサイクルフック
`keep-alive`を使っている時には、以下のライフサイクルフックが使えるようになるので、それらを使って「～の時に」という実装をするようにする。

| ライフサイクルフック | 説明 |
|:-:|---|
| activated | コンポーネントがDOMに表示される時に～ |
| deactivated | コンポーネントがDOMに表示されなくなった時に～ |
![ezgif.com-gif-maker (10).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/5cb18b6b-9261-c020-ff72-2d1f01a11f4a.gif)

動画のソースコードは以下（`App.vue`は[`<keep-alive></keep-alive>`タグで囲む事で`destroyed`されないようにできる](#keep-alivekeep-aliveタグで囲む事でdestroyedされないようにできる)と同じ）。

ソースコード全体は以下。

https://github.com/yuta-katayama-23/intro-vuecli/tree/8373cd49df7eb9d18b773a1276bf91549309c362

## Vue.jsの勉強メモ一覧記事へのリンク
Vue.jsについて勉強した際に書いた勉強メモ記事のリンクを集約した記事。

- https://qiita.com/yuta-katayama-23/items/dabefb59d16a83f1a1d4