先取りVue 3.x !!  Composition API を試してみる 

Vue.js v3.xのRFC（Request for Comments） となっている[Composition API](https://github.com/vuejs/composition-api) を色々触ってみたのでまとめます。

# Vue Composition API とは？

> Introducing the Composition API: a set of additive, function-based APIs that allow flexible composition of component logic

https://vue-composition-api-rfc.netlify.com/#summary

とある通り、コンポーネントのロジックの柔軟なコンポジションを可能にする関数ベースのAPIです。
型推論の改善と、合成関数によるロジックの整理が可能になっています。
`@vue/composition-api`を追加することで、Vue2系でも使用することができます。


# 環境構築

`vue-cli` でプロジェクトを作成します。
ポイントは`Manually select features`を選び`TypeScript`を選択すること。`class-component`は使わないので `N` を選んで下さい。

```bash
$ vue create practice-composition-api 
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? No # class-componentの利用では `N`を選んで下さい
...
$ cd practice-composition-api
$ yarn add @vue/composition-api 
```

続いて `main.ts` で`@vue/composition-api` の利用を宣言します。

```typescript:src/main.ts
import Vue from "Vue";
import VueCompositionApi from "@vue/composition-api";
Vue.use(VueCompositionApi);
```

これでComposition APIを使う準備はOKです。

# コンポーネントの作成 ~ defineComponent ~

`defineComponent` 関数によりコンポーネントを作成する事で、型推論が効くようになります。
また、今までの`methods`、`data`、`ライフサイクルフック`などは全て、`defineComponent` に渡すオブジェクトの`setup()` 関数内で宣言します。

```vue:sample.vue
<template>
...
</template>

<script lang="ts">
import { defineComponent } from "@vue/composition-api";

export default defineComponent({
  setup() {
    // ここにリアクティブなデータ、関数を定義
  }
})
</script>
```

# リアクティブなプロパティの宣言 ref, reactive
今までの`data()`に相当するリアクティブなプロパティは、`setup()`内で`ref()`または`reactive()`で宣言します。
（正確には、`reactive`は`vue.observable()`相当）
`ref`は今回のRFCで新しく出た概念で、`toRefs`や、`isRef`など新しいメソッドが追加されています。

`<template>` で使う値・関数は必ず、**`setup()`のreturnでオブジェクトとして返す** ことを忘れないで下さい。
今までと異なり冗長な気もしますが、スコープが狭まるので個人的には良いと思っています。

以下、`messageOne`と`messageTwo`はどちらもリアクティブな値です。
サンプルではreactive, refともに、ジェネリクスで型注釈加えていますが、記載しなくても型推論が効きます。

```vue
<template>
  <div>
    <h2>{{ state.messageOne }}</h2>
    <h2>{{ messageTwo }}</h2>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref } from "@vue/composition-api";

export default defineComponent({
  setup() {
    const state = reactive<{ messageOne: string }>({
      messageOne: "Hello"
    });
    const messageTwo = ref<string>("こんにちは");

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

```

ここで気になるのが、`reactive()`と、`ref()`の使い分けですが、ドキュメントによるとどちらの動作も理解した上で使い分けることが良いと記載されています。

https://vue-composition-api-rfc.netlify.com/#ref-vs-reactive

私自身まだ、あまり良く理解できてないので、また後で別記事でまとめたいです。

# コンポーネント間での値の受け渡し props, emit

`props`は`defineComponent()`に渡すオブジェクト内で、`props`プロパティとして宣言します。
今までと同様に、`props`に対してtypeや、default、requiredなどの制約も設定できます。
型推論を効かせたい場合は、別に型を定義して、`setup()`の引数として`props`を渡す際に型アノテーションをつけます。

```typescript

type Props = {
  message: string;
};

export default defineComponent({
  props: {
    message: {
      type: String,
      default: "default Value"
    }
  },
  setup(props: Props) {
    props.message //string型として型推論される
  }
})
```

`emit`は`setup()`に渡す第2引数`context`のメソッドとして使用できます。
型定義は `SetupContext`です。他にも今まで `this` に対して呼んでいた `slot`や `parent`, `root`なども,`context`から呼ぶことができます。

```typescript
  setup(props: Props, context: SetupContext) {
    const emitSample = () => {
      context.emit("emit-sample", "サンプル");
    };
  }
```

以下、実際に `props` と `emit` を使ったサンプルです。
子コンポーネントのボタンで、親から受け取った値を変形し、`emit` で伝播させています。

```vue:ChildComponent.vue
<template>
  <div>
    <button @click="upperCaseMessage">To upper case</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, SetupContext } from "@vue/composition-api";

type Props = {
  message: string;
};

export default defineComponent({
  props: {
    message: {
      type: String,
      default: "default Value"
    }
  },
  setup(props: Props, context: SetupContext) {
    const upperCaseMessage = () => {
      context.emit("change-message", props.message.toUpperCase());
    };

    return {
      upperCaseMessage
    };
  }
});
</script>
```

```vue:ParentComponent.vue
<template>
  <div>
    <h1>{{ state.message }}</h1>
    <ChildComponent
      :message="state.message"
      @change-message="changeMessage"
    ></ChildComponent>
  </div>
</template>

<script lang="ts">
import ChildComponent from "@/components/ChildComponent.vue";
import { defineComponent, reactive } from "@vue/composition-api";

export default defineComponent({
  components: {
    ChildComponent
  },
  setup() {
    const state = reactive({
      message: "Hello"
    });

    const changeMessage = (message: string) => {
      state.message = message;
    };

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

```

# ライフサイクルフック
vueのライフサイクルに合わせて処理を実行するライフサイクルフックは、`setup()`で、`onXXX`の形式で設定できます。

```typescript
import { onMounted, onUpdated, onUnmounted } from 'vue'

const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
```

以下前のインターフェイスとの対応表です。
created系は、setup関数に内包されたことに注意してください。

|Vue 2.x| Compostion API|
|---|---|
|beforeCreate| - |
|created| - |
|beforeMount| onBeforeMount |
|beforeUpdate| onBeforeUpdate |
|beforeDestroy| onBeforeUnmount |
|destroyed| onUnmounted |
|errorCaptured | onErrorCaptured |

その他新たに`onRenderTracked`, `onRenderTriggered`も追加されているようです。（まだ未確認）

# おわりに

以上、Vue Composition APIの簡単な紹介でした。
デコレータを多用するClass Componentよりだいぶ分かりやすいインターフェースになった気がします。
`defineComponent`を使えば、型注釈ほぼ必要なく自然に型推論が効くのも良いです。

まだまだ、検討中ですが採用されて、より使いやすいVueになると良いですね。

# 参考

公式ドキュメント 
分かりやすく情報がまとまっているのでオススメです。

https://vue-composition-api-rfc.netlify.com/