初心者から見たVue3コンポーネントの実装のしやすさについて Vision.3

# Vue3コンポーネントの実装のしやすさについて Vision.3

Vue3のコンポーネントの実装について、前回はVue3コンポーネントの概略から、
propsの受け渡しとイベントの購読について、
引き続きシンプルな実装を体験しました。

前回：propsの受け渡しとイベントの購読について

https://qiita.com/babel_0101/items/05261108683e9d59dd9d

今回は、スロットを使ったコンテンツ配信と動的コンポーネントを以下にまとめます。
## Vue3のコンポーネントの基礎3

公式ページ：[コンポーネントの基礎](https://ja.vuejs.org/guide/essentials/component-basics.html#content-distribution-with-slots)を読み進めながら、以下のように解釈。

1. **スロットを使ったコンテンツ配信(Content Distribution with Slots):** <br>子コンポーネント側にカスタム要素`<slot>`を設置しておくと、<br>以下のように親コンポーネント側から単純にコンテンツを渡せます。<br>公式ページの記載を更に単純化すると、以下の通りです。 

   ```vuejs:child.vue 
   <template>
     <div>
        ・・子コンポーネント側の実装
       <slot />
     </div>
   </template>
   ```
   ```vuejs:parent.vue
   <ChildContent>
     子コンポーネントに表示させたい何かのコンテンツ 
   </ChildContent>
   ```
   <br>
1. **動的コンポーネント(Dynamic Components):** 任意のコンポーネントに<br>`<component :is="...">`を設置しておくと、動的に表示を変更できる部品になります。<br>公式ページでは、タブ切り替え部品が紹介されていました。タブ切り替え部品では<br>`:is`に以下のいずれかの値を含めることができます。
    1. 登録されたコンポーネントの文字列
    1. 実際にインポートされたコンポーネントオブジェクト

    ただし、`<component :is="...">`を設置するだけでは、タブを切り替えたタイミングで非表示になったコンテンツの入力値は消えてしまいます。
    `<KeepAlive>`要素で`<component :is="...">`を囲んでおくと、タブを切り替えて非表示になったコンテンツの入力値も保持できます。入力値を保持すべきか、破棄すべきか、画面の仕様によって使い分けることができそうです。入力値保持の実装および動作実験は、別途行いますので、本記事内では扱いません。
    ```vuejs:dynamic.vue
    <KeepAlive>
     <component :is="activeComponent" />
    </KeepAlive>
    ```
## 実装してみた
### slot
`slot`を設置した子コンポーネントを一つ作ります。
![スクリーンショット (444).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/a74f6382-aa9d-b062-a5dc-ff012dad8aff.png)

親コンポーネントに設置します。
![スクリーンショット (447).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/ac852aac-0c9a-8972-c50f-ffea458cd264.png)

### slot動作実験
簡単な記述で子コンポーネントに、親コンポーネントで指定した<br>コンテンツ（今回は単純な文字列）を表示できました。
![スクリーンショット (448).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/abe87ace-3317-efa7-6169-04a0705d7dd6.png)
### Dynamic Components
まず動的コンポーネント例として、タブ切り替え用の単純な子コンポーネントを3つ作ります。
```vuejs:TabA.vue
<template>
  <div class="tab">
    Tab A コンポーネント
  </div>
</template>
```
```vuejs:TabB.vue
<template>
  <div class="tab">
    Tab B コンポーネント
  </div>
</template>
```
```vuejs:TabC.vue
<template>
  <div class="tab">
    Tab C コンポーネント
  </div>
</template>
```
タブコンポーネントを呼び出す親コンポーネントを一つ作ります。

![スクリーンショット (450).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/6cbe4881-4a95-3466-a36d-ce311f38b1d0.png)
※CSS部は任意の見栄えで良いので、今回は省略します。
### Dynamic Components動作実験
初期状態はタブAを表示します。
![スクリーンショット (451).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/50a3f055-c343-639a-5f18-0ed4b2bc55e1.png)
タブBに切り替えられます。
![スクリーンショット (452).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/389dc38b-6077-4f58-8b9b-2e0a0e5022ea.png)
タブCにも切り替えられます。
![スクリーンショット (453).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3813799/d1953ce4-8d84-bb16-6113-9a0da8f3b0f4.png)
簡単な要素を並べるだけで、タブ遷移も実行できました。今回もコンポーネントという単位では全く困らずに実装できています。

## 補足
実はタブ遷移用のデモ画面へのルーティング設定中、
> 宣言ファイルが見つかりませんでした。……(中略)…… ts(7016) 

というエラーに遭遇しました。script setupの`lang="ts"`が抜けている<br>コンポーネントがあったせいでした。もう一つ

> 型 'string' の式を使用して型 '{ TabA: DefineComponent<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<ExtractPropTypes<{}>>, {}, {}>; TabB: DefineComponent<...>; TabC: DefineComponent<...>; }' にインデックスを付けることはできないため、要素は暗黙的に 'any' 型になります。
  型 'string' のパラメーターを持つインデックス シグネチャが型 '{ TabA: DefineComponent<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, PublicProps, Readonly<ExtractPropTypes<{}>>, {}, {}>; TabB: DefineComponent<...>; TabC: DefineComponent<...>; }' に見つかりませんでした。ts-plugin(7053)

というエラーにも遭遇しました。`"tabs[currentTab]"`のような連想配列の<br>オブジェクト検索は型が判定できず、許されないようです。<br>`"tabs[currentTab as keyof typeof tabs]"`と記載して型を指定する<br>ことで対応しました。

エラー対応については、本筋からずれてしまうので<br>上記、紹介だけに留めますが、実装がほんの少し、複雑になってくると<br>各種エラーにも対応する必要が出てきます。エラーを放置したまま、先に進むと<br>別のテーマにおいてもエラーが邪魔になり、学習が進まなくなりますので、少し<br>時間をかけても、エラーが発生した場合は、丁寧に対応した方が幸せになります。

次回：DOM内テンプレート解析の注意点

https://qiita.com/babel_0101/items/6713112a20778cc0423c