VueのProviderパターンを理解するためのサンプルコード

## はじめに
先日大変わかりやすいこちらの記事を拝見しました。

https://qiita.com/karamage/items/4bc90f637487d3fcecf0#composition-api%E3%82%92%E4%BD%BF%E3%81%88%E3%81%B0%E7%8A%B6%E6%85%8B%E3%81%A8%E3%83%AD%E3%82%B8%E3%83%83%E3%82%AF%E3%82%92%E5%88%87%E3%82%8A%E5%87%BA%E3%81%9B%E3%82%8B

こちらの記事ではカウンターアプリをさまざまなパターンで実装しています。
1. view, 状態, ロジックが混在している
1. 状態とロジックを切り出す
1. Providerパターンを使って状態を共有する

自分でもこの3パターンを書いて理解を深めようと思います。

## 環境
`nuxt 3.9.3`
`vue 3.4.14`
`unocss 0.58.3`

## 作るもの
シンプルなカウンターアプリを作成します。
初期値は0でプラスボタンをクリックすると1増えて、マイナスボタンをクリックすると1減ります。

## view, 状態, ロジックが混在しているパターン
一つのファイルにview (`<span>{{ count }}</span>`), 状態 (`const count = ref<number>(0)`), ロジック (`const increment = () => count.value++`)が混在していますね。

```vue:pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <span>{{ count }}</span>
      <div flex="~ row" items="center" gap="8px">
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const count = ref<number>(0)

const increment = () => count.value++
const decrement = () => count.value--
</script>
```

## 状態とロジックを切り出すパターン
状態とロジックは`useCounter`へ切り出しています。

```ts:composables/useCounter.ts
export function useCounter() {
  const count = ref<number>(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count,
    increment,
    decrement
  }
}
```

```vue:pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <span>{{ count }}</span>
      <div flex="~ row" items="center" gap="8px">
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
const { count, increment, decrement } = useCounter();
</script>
```

### Providerパターン
Providerパターンを使って表示用のコンポーネント, インクリメントボタン, デクリメントボタン, ロジック (useCounter) に分けます。

```ts:composables/useCounter.ts
import type { InjectionKey } from "vue"

export const counterKey: InjectionKey<Counter> = Symbol('counter')

export function useCounter() {
  const count = ref<number>(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count,
    increment,
    decrement
  }
}

export type Counter = ReturnType<typeof useCounter>
```

```vue:components/counterDisplay.vue
<template>
  <div>
    <span>{{ counter?.count }}</span>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
```

```vue:components/counterIncrement.vue
<template>
  <div>
    <button @click="counter?.increment">+</button>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
```

```vue:components/counterDecrement.vue
<template>
  <div>
    <button @click="counter?.decrement">-</button>
  </div>
</template>

<script setup lang="ts">
const counter = inject<Counter>(counterKey)
</script>
```

```vue:pages/index.vue
<template>
  <div min-h="100vh" flex="~" items="center" justify="center">
    <div flex="~ col" gap="8px" items="center">
      <CounterDisplay />
      <div flex="~ row" items="center" gap="8px">
        <CounterIncrement />
        <CounterDecrement />
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
provide(counterKey, useCounter())
</script>
```