Nuxt.jsのasyncDataをComposition APIで


[前回の記事](https://qiita.com/relishable/items/b458d0e291804edddddd)から続いて、Nuxt.js周りの実験中です。

vue3で正式導入予定の`composition-api`をNuxt.jsで試してみました。

## nuxtjs/composition-api のインストール

Nuxt.js用のcomposition-apiをインストール。
※ 9/3時点で`v0.12.2`とバージョンも低くこれから色々変わっていく可能性があるので注意です。

```
$ npm install @nuxtjs/composition-api --save
```

## nuxt.config.js に設定追加

buildModulesオプションに追記

```nuxt.config.js
{
  buildModules: [
    '@nuxtjs/composition-api'
  ]
}
```

## jest用の設定を追加

※ この記事では、jestについては書かないのですが、今後書くので一応。

```jest.config.js
moduleNameMapper: {
  '@nuxtjs/composition-api': '@nuxtjs/composition-api/lib/cjs/entrypoint.js',
},
```


## composition-apiの形式に既存のコンポーネントを書き換え

Nuxt.js + buefyでインストールした場合に
デフォルトで作成されている、components/Card.vueを以下のように書き換えます。

```components/Card.vue
<template>
  <div class="column">
    <div class="card">
      <header class="card-header">
        <p class="card-header-title has-text-grey">
          {{ state.card.title }}
        </p>
      </header>
      <div class="card-content">
        <div class="content has-text-centered">
          <b-icon :icon="state.card.icon" size="is-large" type="is-primary" />
        </div>
      </div>
      <footer class="card-footer">
        <div class="card-footer-item">
          <span v-html="state.card.content"></span>
        </div>
      </footer>
    </div>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, reactive } from '@nuxtjs/composition-api'

export default defineComponent({
  props: {
    title: {
      type: String,
      required: true,
    },
    icon: {
      type: String,
      required: true,
    },
    content: {
      type: String,
      required: true,
    },
  },
  setup(props) {
    const state = reactive({
      card: computed(() => props),
    })

    return { state }
  },
})
</script>
```

## storybook でコンポーネントの確認

storybookで作成したコンポーネントのpropsの書き換えなどが正常に動作するかを確認します。
[Controls addon](https://storybook.js.org/docs/react/essentials/controls)を使ってpropsの書き換えができるようにしました。


```components/Card.stories.ts
import Card from './Card.vue'

export default {
  title: 'Card',
  argTypes: {
    title: {
      name: 'title',
      defaultValue: 'Free',
      control: {
        type: 'text',
      },
    },
    icon: {
      name: 'icon',
      defaultValue: 'github',
      control: {
        type: 'select',
        options: [
          'github',
          'cellphone-link',
          'alert-decagram',
          'arrange-bring-to-front',
        ],
      },
    },
    content: {
      name: 'content',
      defaultValue: '<a href="https://github.com/relishable">github</a>',
      control: {
        type: 'text',
      },
    },
  },
}

export const Normal = (args: any, { argTypes }: { argTypes: any }) => ({
  components: { Card },
  template:
    '<card :title="title" :icon="icon" :content="content"></card>',
  props: Object.keys(argTypes),
})

```

### storybookを起動して動作確認

```
$ npx nuxt storybook
```

![compostion-storybook.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10344/b09de060-6461-7f6b-808a-f6923fd3ef25.gif)


## pages以下で非同期データを取得して、画面表示してみる

今回のメインとなるところ。
今まで、`asyncData`メソッドでやっていた非同期データ取得を`composition-api`でやってみました。

pagesに以下のcard.vueというのを作り、非同期データ用にstatic/json/data.jsonを読み込んでます。

`nuxtjs/composition-api`が用意してくれている

* useContext
* useAsync
* useMeta

を使ってやってみました。

```pages/card.vue
<template>
  <section class="section">
    <div class="columns">
      <template v-if="state.isLoading">読込中....</template>
      <template v-else>
        <card
          :title="card.title"
          :icon="card.icon"
          :content="card.content"
          :key="card.title"
          v-for="card in state.cards"
        >
        </card>
      </template>
    </div>
  </section>
</template>

<script lang="ts">
import {
  defineComponent,
  reactive,
  useAsync,
  useContext,
  useMeta,
} from '@nuxtjs/composition-api'
import Card from '../components/Card.vue'

export default defineComponent({
  name: 'Cards',
  head: {},
  components: {
    Card,
  },
  props: {},
  setup() {
    const { app } = useContext()
    const state = reactive({
      cards: [],
      isLoading: true,
    })

    useAsync(async () => {
      state.cards = await app.$axios.get('/json/data.json').then((res: any) => {
        return res.data.cards
      })
      state.isLoading = false
    })

    useMeta({ title: 'カード一覧' })

    return { state }
  },
})
</script>

```

### 起動して動作確認

```
$ npm run dev
```

アクセスすると一瞬、読み込み中....が表示されたあとカード一覧が表示されました。

![スクリーンショット 2020-09-04 0.25.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10344/92fb7b33-a07a-6e07-ab60-ca0fb4b98c90.png)