Vue.js2.6.x以上で実装するテンプレートとアトミックデザインの手法

## 概要

JSフレームワークであるVue.jsでもReactでも、アトミックデザインを採用して実装する案件にアサインすることが多くなってきました。
そこでフロントエンドでの範囲に限られますが、バックエンドから取得したデータを出力するまでの手法として、各テンプレートの構成や処理内容について紐解いていこうと思います。

### バージョン環境

```
vue@2.6.14
```


### ソース構成

```
App.vue
├ views
　　└ page.tpl //大元の画面
├ components
　　├ Heading.tpl //見出しを出力
　　├ Table.tpl //テーブル全体を出力
　　└ tableList.tpl //テーブルのリストを出力
└ assets/css
　　├ style.scss
　　└ page.scss
```

画面に出力される大枠のテンプレートは `page.tpl` です。
これとヘッダー、フッターなどを組み合わせてページ全体を構成する形になるため、アトミックデザインの「Organisms」にあたります。

その中の構成コンポーネントとしてテーブルを出力する `Table.tpl` 、その中のリストを出力する `tableList` などが存在します。
中身のボリュームはものによって変わるので、アトミックデザインの「Atoms」や「Molecules」にあたります。

また、スタイライズについては共通のものを使用し、 `style.scss`で全体管理をしつつ各画面ごとにスタイルシートを適用する形のイメージです。
（今回のテンプレートの場合は `page.scss` ）


### 画面のベース構成

まず大元となる `page.tpl` を見ていきます。

```vuejs:src/views/page.tpl
<template>
  <!-- テンプレートの中身を記述 -->
  <Title type="h2">
    見出し
  </Title>
  <Table
    :items="items.listItems"
  />
</template>

<script>

// テンプレートで呼び出すコンポーネントを指定
import Table from '@/components/Table';
import Title from '@/components/Title';

export default {

  data() {
    return {
      //初期値、データ型を設定する
      items: {
        listItems: [],
      }
    };
  },

  components: {
    // テンプレート内で呼び出すコンポーネントを指定
    Table
  },

  created() {
    // テンプレートが呼び出されたタイミングで実行するファンクション
    this.getListItems();
  },

  computed: {
    // 子コンポーネントに返り値を渡す際のファンクション
  },

  methods: {
    // テンプレート内で実行するファンクションの内容を定義
    getListItems() {
      this.items.listItems = [APIで取得したデータ];
    },
    
  }
};
</script>
```

```vuejs:src/components/Title.tpl
<template>
  <h2 v-if="type == 'h2'">
    <slot></slot>
  </h2>
  <h3 v-else-if="type == 'h3'">
    <slot></slot>
  </h3>
  <h4 v-else-if="type == 'h4'">
    <slot></slot>
  </h4>
</template>

<script>
  props: {
    // 親から受け取るプロパティの型定義
    type: {
      type: String,
      required: false
    }
  }
</script>
```

pageでは２つのコンポーネントを呼び出します。
見出しを出力する `Title` 、テーブルを出力する `Table` です。

Titleコンポーネントについては記述したテキストを元にh2〜h4の見出しを出力します。
どのタグを出すかは、親テンプレートで指定したプロパティ `type` を参照する形になっています。
ミニマムなコンポーネントに対して親側で要素を設計する場合は、子供側で `slot` を記述することで構成にあった出力をすることができます。

Tableについては `:items` プロパティを設定していますが、これはcreatedのタイミングでmethodsの `getListItems` によりAPIデータが取得され、dataに入ったものを参照しているという流れになります。

なおこの次にここで取得したデータを`tableList`で一覧表示することになるのですが、tableListはpageと直接的な連携を行うわけではないのでここではimportなどの記述が必要ありません。
Tableを介してtableListがデータを取得する流れができていればいいというわけです。



### テーブル部分の構成

```vuejs:src/components/Table.tpl
<template>
  <table>
    <tr>
      <th>項目1</th>
      <th>項目2</th>
      <th>項目3</th>
      <th>項目4</th>
    </tr>
    <tableList
      v-for="item in items"
      :key="item.id"
      :item="item"
    />
  </table>
</template>

<script>
import tableList from '@/components/tableList';

export default {
  props: {
    items: {
      type: Array,
      required: true
    }
  },

  components: {
    tableList
  }
};
</script>
```

```vuejs:src/components/tableList.tpl
<template>
  <table>
    <tr>
      <th>{item.data1}</th>
      <th>{item.data2}</th>
      <th>{item.data3}</th>
      <th>{item.data4}</th>
    </tr>
  </table>
</template>

<script>

export default {
  props: {
    item: {
      type: Object,
      required: true
    }
  }
  
};
</script>
```

まず`Table`では、`page`で取得されたデータリスト「listItems」を`items`という配列で継承しています。
このitemsは`tableList`への継承データとなり、tableListをimportした状態で`v-for`でループ出力させる構造になっています。

表示順は`:key`プロパティでの指定が可能で、今回はAPIデータに含まれる`item.id`を指定しています。

次に`tableList`側ではそのデータをpropsで受け取り、４つある項目にそれぞれの値を出力している形になります。
ここの`data1`や`data2`といった値は、データの大元であるAPIデータ側の構成に準拠した名前となります。


### 参考文献

- [https://jp.vuejs.org/v2/guide/syntax.html](https://jp.vuejs.org/v2/guide/syntax.html)
- [アトミックデザインとは？5つのコンポーネントから作るUIデザイン手法](https://www.creativevillage.ne.jp/94262)