【Vue.js】Vue CLI でタブ機能を作る（動的コンポーネント）

# はじめに
Vue.js を学習していて、動的コンポーネント(`:is=""`)を活用して、タブ機能を作成してみました。
（Vue CLI を使用）

こういうやつ↓↓
<img width="887" alt="スクリーンショット 2020-07-22 18.50.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/0320466d-263f-2738-95e5-c225b471168c.png">

# Vue CLI ディレクトリ構造
<img width="168" alt="スクリーンショット 2020-07-22 18.49.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621162/6b57ad35-45d6-bb4d-6ea1-6a538f709f1c.png">

# 主要ファイルの記述
以下に主要ファイルのコードを記載する

- index.html
- main.js
- Tab1.vue
- Tab2.vue
- Tab3.vue
- App.vue

```index.html
<!DOCTYPE html>
<html lang="en">
  <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">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title>Vue.js タブ</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
  </body>
</html>
```

```main.js
import Vue from 'vue';
import App from './App.vue';

new Vue({
  render: h => h(App), // render関数にてApp.vueをレンダリング
}).$mount('#app') // index.htmlのid=appをマウント

```

App.vueの子コンポーネントファイルを3つ用意

```Tab1.vue
<template>
  <div>
    <p>タブ１だよ</p>
  </div>
</template>
```

```Tab2.vue
<template>
  <div>
    <p>タブ２だよ</p>
  </div>
</template>
```

```Tab3.vue
<template>
  <div>
    <p>タブ３だよ</p>
  </div>
</template>
```
上記3つの子コンポーネントの親コンポーネントファイルが App.vue

```App.vue
<template>
  <div>
    <ul class="tabs-menu">
      <li
        :class="{ active: currentTab === 'Tab1' }"
        @click="currentTab = 'Tab1'"
      >タブ１</li>
      <li
        :class="{ active: currentTab === 'Tab2' }"
        @click="currentTab = 'Tab2'"
      >タブ２</li>
      <li
        :class="{ active: currentTab === 'Tab3' }"
        @click="currentTab = 'Tab3'"
      >タブ３</li>
    </ul>
    <div class="tabs-content">
      <component :is="currentTab"></component>
    </div>
  </div>
</template>

<script>
import Tab1 from "./components/Tab1.vue";
import Tab2 from "./components/Tab2.vue";
import Tab3 from "./components/Tab3.vue";

export default {
  data() {
    return {
      currentTab: 'Tab1',
    };
  },
  components: {
    Tab1,
    Tab2,
    Tab3,
  }
}
</script>

<style scoped>
body {
  color: #3f4548;
}

/* ulのデフォルトスタイルを消去 */
.tabs-menu {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

/* タブの基本スタイル */
.tabs-menu li {
  display: block;
  float: left;
  margin-right: 8px;
  margin-bottom: -1px;
  padding: 10px 20px;
  border-style: solid;
  border-width: 1px;
  border-color: transparent;
  border-radius: 4px 4px 0 0;
  color: #557f95;
  text-decoration: none;
}

/* タブにマウスを乗せたらカーソルの形を変える */
.tabs-menu li:hover {
  cursor: pointer;
}

/* 非選択のタブにマウスを乗せたら色を変える */
.tabs-menu li:not(.active):hover {
  border-color: #f0f0f0 #f0f0f0 #999;
  background-color: #f0f0f0;
}

/* 選択中のタブ */
.tabs-menu .active {
  border-color: #999 #999 transparent #999;
  background-color: #f0fbff;
  color: black;
}

/* タブコンテンツ表示エリア */
.tabs-content {
  clear: both;
  background-color: #f0fbff;
  border: 1px solid #999;
  border-radius: 0 4px 4px 4px;
  padding: 10px 10px 30px 10px;
}
</style>
```
以下で App.vue について解説↓↓

# App.vue について解説
子コンポーネントファイルを読み込み

```App.vue
<!-- 一部抜粋 -->
<script>
import Tab1 from "./components/Tab1.vue";
import Tab2 from "./components/Tab2.vue";
import Tab3 from "./components/Tab3.vue";

// 省略

</script>
```

コンポーネントをローカル登録

```App.vue
<!-- 一部抜粋 -->
<script>
  components: {
    Tab1,
    Tab2,
    Tab3,
  }
}
</script>
```

表示タブの初期値はTab1に設定

```App.vue
<!-- 一部抜粋 -->
<script>
export default {
  data() {
    return {
      currentTab: 'Tab1',
    };
  },
}
</script>
```

`@click="currentTab = 'TabX'"`でタブをクリックすると `currentTab` の値にコンポーネント名を代入する。

そして、`<component :is="currentTab"></component>` の`currentTab` に代入されている値によって表示するコンポーネントを判別させている。

`:class="{ active: currentTab === 'Tab1' }"`
は`currentTab`が`Tab1`であれば`active`クラスを付与している。

```App.vue
<template>
  <div>
    <ul class="tabs-menu">
      <li
        :class="{ active: currentTab === 'Tab1' }"
        @click="currentTab = 'Tab1'"
      >タブ１</li>
      <li
        :class="{ active: currentTab === 'Tab2' }"
        @click="currentTab = 'Tab2'"
      >タブ２</li>
      <li
        :class="{ active: currentTab === 'Tab3' }"
        @click="currentTab = 'Tab3'"
      >タブ３</li>
    </ul>
    <div class="tabs-content">
      <component :is="currentTab"></component> <!-- ここで表示するコンポーネントを動的に切り替えている -->    
    </div>
  </div>
</template>
```

# 最後に

`<component :is="コンポーネント名"></component>`を使えば、`v-if` `v-show`を使わずに簡単に動的にコンポーネントの切り替えができた。

ご指摘などありましたら、お気軽にお願いします。

●参考リファレンス
[Vue.js 動的 & 非動的コンポーネント](https://jp.vuejs.org/v2/guide/components-dynamic-async.html)