Nuxt + TypeScript + CompositionAPIのstore

Nuxt + TypeScript + CompositionAPIについて興味が出てきたのでいろいろ調べながら進めてきました。

まずは、色々頑張って構築とかしようかと思ったのですがなかなか時間は思うように作れず超中途半端になりました。

一応ソースコードを上げておくので雰囲気で誰かの一部参考になればなと思います。
- https://github.com/usu-blog/firebase-cms

## TypeScriptの書き方は3種類

NuxtでTypeScriptを書く方法は3種類あります。

1. Options API  
  従来の記述の仕方です。
2. Class API  
  デコレータを使用したコンポーネントをクラスとして利用する書き方
3. Composition API  
  Vue3で採用されると噂のモダンな書き方

Composition API の書き方で何も問題なければ今後はComposition APIの書き方を勉強しながら使っていけばVue3がきたときもスムーズに移行できて良いかと思っています。

## storeについて

Nuxtの機能の１つのstoreは自動でvuexの名前空間の連携をしてくれます。  
しかしComposition APIではvuexを推奨してないっぽい感じでprovideとinjectを使って変数や関数を渡してやりとりするのが推奨されているようです。

> Plugin Development
Many Vue plugins today inject properties onto this. For example, Vue Router injects this.$route and this.$router, and Vuex injects this.$store. This has made type inference tricky since each plugin requires the user to augment the Vue typing for injected properties.

> When using the Composition API, there is no this. Instead, plugins will leverage provide and inject internally and expose a composition function. The following is hypothetical code for a plugin:

- 参考: https://vue-composition-api-rfc.netlify.com/#usage-alongside-existing-api

なのでNuxtの機能のstoreの部分は、ただのstoreのディレクトリとして運用する感じになりそうです。

### 実際のソースコード

provideで宣言したものは子コンポーネントに共有できます。

以下のコードのようにまず`store`ディレクトリにprovideするものを書いておきます。

```ts:~/store/contents.ts
import { ref } from '@vue/composition-api'

export default function contentsStore() {
  const state = {
    counter: ref(0),
    posts: ref([
      {
        id: 0,
        title: 'test',
        slug: 'test',
        category: 'nuxt',
        state: 'drafted'
      }
    ]),
    pages: ref([
      {
        id: 0,
        title: 'test',
        slug: 'test',
        state: 'drafted'
      }
    ]),
    users: ref([
      {
        id: 0,
        name: '田中 太郎',
        email: 'tanaka@sample.com'
      },
      {
        id: 1,
        name: '佐藤 次郎',
        email: 'sato@sample.com'
      },
      {
        id: 2,
        name: '斎藤 三郎',
        email: 'saito@sample.com'
      }
    ])
  }
  return { ...state }
}
```

次にprovideする箇所ですが、nuxtの場合はdefaultに宣言しておけば`pages`でもpagesから呼び出されるコンポーネントでもinjectで呼び出して利用できます。

```vue:~/layouts/default.vue
...
<script lang="ts">
import { createComponent, ref, provide } from '@vue/composition-api'
import contentsStore from '~/store/contents'

export default createComponent({
  components: { Drawer, Footer },
  setup() {
    // ↓でprovide
    provide('ContentsStore', contentsStore())
    const state = {
      drawer: ref(false),
      title: ref('Firebase CMS'),
      items: ref([{ title: 'Sign Out' }])
    }
    return { ...state }
  }
})
</script>
```

以下が実際にinjectでprovideしたstoreディレクトリに書いたものをとってきている箇所です。  

```vue:~/pages/index.vue
...
<script lang="ts">
import { createComponent, ref, inject } from '@vue/composition-api'
export default createComponent({
  setup() {
    const contentsStore: any = inject('ContentsStore')
    const state = {
      labels: ref(['12am', '3am', '6am', '9am', '12pm', '3pm', '6pm', '9pm']),

      value: ref([200, 675, 410, 390, 310, 460, 250, 240])
    }
    return { ...state, ...contentsStore }
  }
})
</script>
```

型は`any`で取り出していますが、ちゃんとstoreの箇所にtypeやinterfaceを置いておいてそれを持ってくるのがいいと思います。

という感じでVuexを使わずにComposition APIのprovideとinjectで値や値に関係する関数を共有して使えばstoreの代替になるよ！という話でした。