【Vue.js】Composition APIを利用したコンポーネントの作成方法について

# Composition APIとは
従来のdata, computedを用いたAPIによる問題を改善するための、Vue.jsの新たなコンポーネント作成方法です。
https://vue-composition-api-rfc.netlify.com/#summary

Vue3.xで使用可能になるとのことですが、以下のプラグインを使用することで、Vue2.xでも利用可能です。
https://github.com/vuejs/composition-api

# Composition APIを利用したコンポーネントの例
```vuejs
<template>
  <div>
    <p>{{ reactiveValue }}</p>
    <p>{{ computedValue }}</p>
    <div>
      <h1>hoge</h1>
      <p>{{ hogeMethod() }}</p>
      <p>{{ hogeValue }}</p>
    </div>
    <div>
      <h1>fuga</h1>
      <p>{{ fugaMethod() }}</p>
      <p>{{ fugaValue }}</p>
    </div>
  </div>
</template>

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

function hoge() {
  const hogeValue = 'hoge'
  const hogeMethod = () => {
    return 'hogeMethod'
  }
  return {
    hogeValue,
    hogeMethod
  }
}

function fuga() {
  const fugaValue = 'fuga'
  const fugaMethod = () => {
    return 'fugaMethod'
  }
  return {
    fugaValue,
    fugaMethod
  }
}

export default createComponent({
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  setup(props, context) {
    onMounted(() => {
      console.log('onMounted')
    })
    const reactiveValue = reactive('reactive value')
    const computedValue = computed(() => props.value)
    const hogeValue = hoge()
    const fugaValue = fuga()
    return {
      computedValue,
      reactiveValue,
      ...hogeValue,
      ...fugaValue
    }
  }
})
</script>
```

# Composition APIを利用してコンポーネントを実装する
ここからは`@vue/composition-api`をベースにComposition APIを見ます。

## setup()
これまでプロパティに記載していた`data`, `computed`, `mounted`は全てsetup()の中に内包することになります。
また、`template`で使用する値は全て`setup`で返却する必要があります。

```typescript
import {
  createComponent,
  computed,
  reactive,
  onMounted
} from '@vue/composition-api'

setup(props, context) {
    // ライフサイクルメソッドもsetup内で実装する
    onMounted(() => {
      console.log('onMounted')
    })
    const reactiveValue = reactive('reactive value')
    
    const computedValue = computed(() => props.value)
    const hogeValue = hoge()
    const fugaValue = fuga()
    return {
      computedValue,
      reactiveValue,
      ...hogeValue,
      ...fugaValue
    }
  }
```
## setupのメリット
Composition APIを使用することによって、データを機能単位にまとめられるというメリットがあります。

従来のAPIでは、1つのコンポーネントが有する機能Aに対して、`data`, `computed`など、プロパティ単位で分割されていました。そのコンポーネントが更に機能B、Cを有する場合、機能に対するデータがあちこちに分散することで、管理が煩雑になってしまいます。

```typescript
data() {
  return {
    機能Aのデータ,
    機能Bのデータ,
    機能Cのデータ
  }
},
computed() {
  機能Aのデータ() {...},
  機能Bのデータ() {...},
  機能Cのデータ() {...}
},
methods: {
...
```

これをComposition APIに置換することで、機能毎にデータをまとめることが出来ます

```typescript
setup() {
  // 機能Aに対する実装
  const A = () => {...}

  // 機能Bに対する実装
  const B = () => {...}

  // 機能Cに対する実装
  const C = () => {...}

  return {
    A,
    B,
    C
  }
}
```

## setupの分割
setupにほとんどの機能を実装するということで、setupの肥大化が懸念されますが、こちらは、機能毎にfunctionを切ることで、肥大化を防止出来ます。
createComponent外で、functionを作成します。

それぞれのfunctionで、templateで使用したい値を返却するのを忘れてはいけません。
最終的には、functionの返り値をそのままsetupの返り値にしてあげます。

```typescript
import {
  createComponent,
  computed,
  reactive,
  onMounted
} from '@vue/composition-api'

function hoge() {
  const hogeValue = 'hoge'
  const hogeMethod = () => {
    return 'hogeMethod'
  }
  return {
    hogeValue,
    hogeMethod
  }
}

function fuga() {
  const fugaValue = 'fuga'
  const fugaMethod = () => {
    return 'fugaMethod'
  }
  return {
    fugaValue,
    fugaMethod
  }
}

export default createComponent({
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  setup(props, context) {
    onMounted(() => {
      console.log('onMounted')
    })
    const reactiveValue = reactive('reactive value')
    const computedValue = computed(() => props.value)
    const hogeValue = hoge()
    const fugaValue = fuga()
    return {
      computedValue,
      reactiveValue,
      ...hogeValue,
      ...fugaValue
    }
  }
})
```

注意点として、機能分離した際の名前の衝突です。
上記例における`hoge()`と`fuga()`に同様のメソッド名がある場合、setupで最後に返却した方に上書きされます。その際には警告などが出力されないため、知らず知らずのうちに上書きが発生する。ということも考えられます。

そのため、機能分離のためのルールを設定するなど、多少人力が必要になるかもしれません。


# 最後に
Composition APIを用いることで、肥大化したコンポーネントで、１機能の修正のために`data`と`methods`をスクロールで往復する状況が改善されるかもしれません。
(そもそも、そのようなコンポーネントは再設計した方がいいかもしれませんが...)


以上です。ありがとうございました。

# 参考記事
https://qiita.com/ryo2132/items/f055679e9974dbc3f977
https://vue-composition-api-rfc.netlify.com/#api-introduction

参考にさせていただきました。ありがとうございました。