らぼるてっく。

てっくてっく歩いてっく。

CompositionAPI vs OptionAPI

はじめに

こんにちは🧑‍💻 ラボルのUIデザイナー/フロントエンドエンジニアの寺岡です。

今回はVue3から新しく使えるようになったComposition APIと、既存のOption APIの比較記事です。 Composition APIは何ができるようになったのか?どういった場面でメリットを感じるのか?を紹介していきます!

目次

どんな人向けの記事か

  • Vueで開発することが多い方
  • OptionAPIでしか書いたことがなく、CompositionAPIの書き方を知らない方

CompositionAPIとは?

特徴

Option(=data()methodsなど)を宣言する代わりに、 使用したい以下の関数をインポートして、Vueコンポーネントを作成できるようにしたもの

UIとロジックの切り離しが可能になった(従来はthisに依存していたので切り離せなかった)

バージョン

  • Vue3以降と2.7のversionで使える機能
  • Vue2でも、@vue/composition-apiを読み込んで使用可能

CompositionAPIのメリット

OptionAPIと比較して、CompositionAPIは以下のメリットがあると感じました。

  • 機能の再利用が可能
  • 可読性が高くなる
  • テストが簡単

countを操作する簡単なコードを例に、それぞれ比較して見ていきましょう。

機能の再利用が可能

前述したように、OptionAPIだとthisへの依存があったのでリアクティブな値を扱うロジックをcomponentから切り離すことが不可能でした。 例えば、足し算(plusCount())の関数を他のcomponentで使いまわしたい時に、OptionAPIだとこれ以上切り離せません。

▼OptionAPIの場合

// App.vue

export default {
  data() {
    return {
      count: 0,
      diff: 1,
    }
  }
  methods: {
    // thisに依存しているため、これ以上切り離せない🤔
    plusCount() {
      this.count = this.count + this.diff
    },
  },
}

一方CompositionAPIだと、以下のようにモジュール用のただのjsファイルで、普通にリアクティブ関数が使えます。 「jsファイルに関数を定義できる」=「いつでも再利用できる」ということになりますね。

▼compositionAPIの場合

// useCount.js

import { reactive } from "vue"

export default function useCount() {
  // jsファイルでも、vueのリアクティブ関数が使用可能🎉
  const state = reactive({
    count: 0,
    diff: 1,
  })
  
  const plusCount = () => {
    state.count = state.count + state.diff
  }
  
  return {
    state,
    plusCount,
  }
}

useCount()をVue component側で呼び出し

// App.vue

import useCount from "./useCount.js"

export default {
  setup() {
    // Vue component側でロジックを利用🔨
    const { state, plusCount } from useCount()
    
    return {
      state,
      plusCount,
    }
  }
}

可読性が高くなる

公式の「なぜ Composition API なのか?」でも提言されているように、柔軟なコード整理が可能です。

単純なロジックのcomponentでは恩恵は感じにくいですが、SFC(単一ファイルコンポーネント)のロジックが複雑化してくると、Option APIの場合ロジックのまとまりが点在する形になり、可読性が悪くなります。

以下の画像の比較を見てみましょう。countmessageに関するロジックがあるとします。 - 紫: countに関するロジック(足し算引き算やリセットなど) - 緑: messageに関するロジック(countを変えたときにメッセージを表示する)

これはOptionAPI→CompositionAPIにリファクタリングしたcomponentのイメージなのですが、コードを同じロジックでまとめることができました。

このcomponentのロジックが複雑化していくと、OptionAPIの方はゴチャゴチャになりそうですよね。 実際に僕もOptionAPIでの開発で、ロジックが複雑なcomponentのコードを改修するときに煩わしさを感じていました。

Aのロジックに関してdataを追加して、methodsのブロックに移動して処理を書き、 Bのロジックもdataに戻って追加し、methodsの処理を書き...

このように、OptionAPIでは一つの機能を実現したいが、記述箇所がcomputedmethodsdataごとにバラバラになってしまいます。

スクロールと⌘ + Fの嵐ですね。これがcompositionAPIだと記述箇所の制限がなくなるので可読性の高いコードが実現可能となります。

テストがしやすい

ここでいう「テストがしやすい」は、つまり「Jestがシンプルに使える」という定義になります。

関数のテストにJestを採用している方は多いと思いますが、Vue componentの中の関数をテストしようとしてつまずいた経験ありませんか? もちろんvue-jestのようなライブラリで可能ですが、できればそういった新規導入のコストなくプレーンなテストをしたいですよね。

前述したように、CompositionAPIだとロジックをjsファイルに切り分けられるので、Jest使ってる方はいつも通りのやり方でテスト可能です。

▼テストしたい関数

// useCount.js

import { ref } from "vue"

// 👇こいつをJestファイルにimportするだけ
export default function useCount() {
  const count = ref(0)
  
  const getCalculatedCount = (diff) => {
    return count.value + diff
  }
  
  return {
    count,
    getCalculatedCount,
  }
}

▼Jestファイル

// useCount.test.js

import useCount from "./useCount.js"

const { getCalculatedCount } = useCount()

test("計算ロジックのテスト", () => {
  // 0 + 2 = 2
  expect(getCalculatedCount(2)).toEqual(2)
}

CompositionAPIのデメリット

一方、OptionAPIと比較して、CompositionAPIのここが惜しい!という部分です - データの参照が冗長 - 複雑なComponentじゃなければ、恩恵を感じにくい

データの参照が冗長

OptionAPIではリアクティブなデータに、一貫してthisでアクセスしてました。

▼OptionAPI

export default {
  data() {
    return {
      count: 0,
      isShowCount: false,
    }
  },
  methods: {
    plusCount(diff) {
      this.count = this.count + diff
    },
    showCount() {
      this.isShowCount = true
    },
  },
}

一方、CompositionAPIだとrefreactiveを使うのでこんな感じです。

▼CompositionAPI

export default {
  setup() {
    const countState = reactive({
      count: 0,
    })
    const isShowCount = ref(false)
    
    const plusCount = (diff) => {
      // 🙂reactiveはObjectなのでこんな感じ
      countState.count = countState.count + diff
    }
    
    const showCount = () => {
      // 🤔refの場合、valueにアクセスする必要がある
      isShowCount.value = true
    }
  }
}

わかりますでしょうか。 reactive関数はObjectなのは見えているのでコード通りにアクセスできますが、 ref関数を使ったリアクティブデータを参照する際、valueにアクセスする必要があるので、少し冗長に感じますね。

複雑なComponentじゃなければ、恩恵を感じにくい(Vue2の経験者のみ)

CompositionAPIは「ロジックを再利用できる」・「可読性の高いコンポーネントを作れる」といったメリットを提示しましたが、 これはあくまで複雑なロジックの時に感じられる恩恵でした。

「ロジックを再利用できる」に関しては、複数コンポーネントで再利用したいロジックがない(=各コンポーネントでロジックが完結してる)場合、恩恵は得られないですね。 「可読性の高いコンポーネントを作れる」に関しても、ロジックが1種類しかないような単純なコンポーネントであれば、OptionAPIとさほど変わりはないですね。

自身のアプリケーションのコードが、これらに当てはまるかどうかを考えてみて、CompositionAPI導入を再検討してみてもいいかもしれません。

まとめ

大枠の比較としては

  • OptionAPIは制限が多く、単純なロジックのアプリケーション向き
  • CompositionAPIは自由度が高く、複雑なロジックのアプリケーション向き

ということがわかりました。

選択肢としてどちらの書き方か迷ったときは、この観点で選択してみてください。

最後に

ラボルでは、エンジニアを積極採用中です。1、2年目のエンジニアから経験豊富なテックリードやエンジニリングマネージャーまで、興味がある方はぜひご応募ください!!

labol.co.jp