Vue Composition APIとTypeScriptの組み合わせ
この記事はギルドワークスAdvent Calendarの3日目の記事です。 Vue.jsのバージョン3にてリリース予定のComposition APIとTypeScriptを組み合わせについて紹介します。
Composition APIとは
Composition APIは関数ベースでコンポーネントを実装する機能です。関数ベースのため、機能の再利用性を高める役割を持っています。他のライブラリではReact hooksやSvelteに近い機能です。
公式のRFCに記載されているモチベーションの1つにTypeScriptとの相性を高めることが書かれています。本記事ではサンプルコードを踏まえてなぜTypeScriptと相性がよくなるのか考えてみます。
https://vue-composition-api-rfc.netlify.com/#better-type-inference
準備
Vue2系で試す場合はプラグインが提供されているので、インストールする。
https://github.com/vuejs/composition-api
$ npm install @vue/composition-api --save or $ yarn add @vue/composition-api
インストールが完了したらVue.use
を追加します。
import Vue from 'vue'; import VueCompositionApi from '@vue/composition-api'; Vue.use(VueCompositionApi);
TypeScriptと併せて使用する、かつVS CodeでVeturを使用している場合、設定を以下のように変更する必要があります。
{ "vetur.useWorkspaceDependencies": true }
Options APIとComposition APIの比較
TypeScriptとの併用がやりやすくなるかを見るために、基本的なVue.jsのAPIであるOptions APIと比較してみたいと思います。 今回は両方のAPIを使って同じ機能を持ったモーダルコンポーネントを実装してみます。 機能としては以下になります。
- モーダルの表示・非表示ができる
- モーダル内に日付を表示し、ボタンによってカウントアップできる
- 日付が条件に達したらメッセージを表示
以下のGitHubにても公開しています。
https://github.com/keinuma/compare-options-and-composition
テンプレート
テンプレートおよびスタイルは共通のコードを使用します。(スタイルのコードは今回の記事では紹介しません。)
<template> <div> <div :class="{ 'modal-overlay': showModal }"> <div v-if="showModal" class="modal-isshow modal-open" > <div class="modal-container"> <button class="close-btn btn btn-outline-info" @click="closeModal"> 閉じる </button> <slot></slot> <div> <div v-show="isLastDate" class="alert alert-danger" role="alert"> <p class="alert-last-date"> クリスマスになりました。<br /> アドベントカレンダーの最終日です。 </p> </div> <p>この記事はギルドワークスアドベントカレンダーの{{ date }}日目の記事です。</p> <button type="button" class="btn btn-info" @click="countUpDate" :disabled="isLastDate"> 次の日 </button> </div> </div> </div> </div> </div> </template>
Options API
Options APIの場合は以下になります。
TypeScript対応は Vue.extend
を使用しています。
import Vue from 'vue' export default Vue.extend({ name: 'ModalOptions', props: { showModal: { type: Boolean, required: true, default: false } }, data() { return { date: 1 } }, computed: { isLastDate() { return this.date === 25 } }, methods: { countUpDate() { if (this.isLastDate) { return } this.date++ }, closeModal(e: Event) { e.stopPropagation() this.date = 1 this.$emit("onCloseModal") } } })
Composition API
Composition APIの場合は以下になります。
基本的に setup
関数の中でデータやメソッドの定義を行います。
リアクティブなデータはComposition APIで定義されている ref
や reactive
関数から定義できます。
モーダルを閉じるメソッドで使用しているように、Vue
インスタンスへは context
を使用してアクセスできます。
import { createComponent, ref, computed } from '@vue/composition-api' type Props = { showModal: boolean } export default createComponent({ props: { showModal: { type: Boolean, required: true, default: false } }, setup(props: Props, context) { const date = ref(1) const isLastDate = computed(() => { return date.value === 25 }) const countUpDate = () => { if (isLastDate.value) { return } date.value++ } const closeModal = (e: Event) => { e.stopPropagation() date.value = 1 context.emit("onCloseModal") } return { showModal: props.showModal, date, isLastDate, countUpDate, closeModal } } })
考察
2つのコードを比較すると、Composition APIはOptions APIに比べて this
がなくなっています。
これまで Vue
の this
にはデータやメソッドのような開発者が定義したアプリケーション的意味合いと、filter
やVue RouterなどのVue
のインスタンス自身の2つの意味を持っていました。Composition APIの場合、前者を setup
関数内で定義、後者を context
に持たせることで this
の責務を分離しています。
責務を分離することで型定義を推論を効かせしやすくしていると考えられます。
また、 Options APIの methods
でも同じことはできるのですが、相互参照が見えやすい setup
関数だからこそ機能としての再利用性を意識しやすくなります。そのため、外部モジュールへの抽出や構造化が促進されると考えられます。
まとめ
- Composition APIによってTypeScriptが使いやすくなる
- Vue 3待ち遠しい