🦁

Vue 2からVue 3移行に伴うOptions APIとComposition APIの比較

に公開

概要

現在参画中のプロジェクトでアプリのフロントエンド開発環境がVue2からVue3に変わろうとしているタイミングに差し掛かったため、これを機に自身もリマインドを書こうと思います。
ver3がベータ版だったこともあり、使用がメジャーになるまでは個人開発でもPENDしている状態だったのですが、こちらも機を見て導入したいです。

本記事の前提

Vue 2では、基本的に Options API という記述方法を使うのがメジャーでした。
Vue 3ではこれに加え、 Composition API という記述方法も使えるようになります。
(Vue 2でも拡張機能として導入が可能だったようですが、マイナーと考えます)

どちらの記述も条件付きで使える状態ではあるため厳密にはバージョンの違いという位置付けではないですが、今回の記事ではメジャーな記法になると考えられる

  • Vue 2: Options API
  • Vue 3: Composition API
    に置き換えて比較してみます。

Options API

公式より引用したソースを元に比較してみます。

まずOptions APIについてですが、おおよその形としては次のような構成があります。

  • components: 外部から参照するコンポーネント、インポートが必要
  • props: 上位コンポーネントから継承するデータプロパティ、変更不可
  • data: 当該コンポーネント内での処理に使用するデータプロパティ、変更可
  • computed: 利用する値が変化すると自動的に再計算される関数
  • watch: 特定の変数やオブジェクトを監視し、値の変化時に自動で処理を実行する
  • methods: 任意のタイミングで呼び出して使用する処理
Options API
// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  data () {
    return {
      repositories: [], // 1
      filters: { ... }, // 3
      searchQuery: '' // 2
    }
  },
  computed: {
    filteredRepositories () { ... }, // 3
    repositoriesMatchingSearchQuery () { ... }, // 2
  },
  watch: {
    user: 'getUserRepositories' // 1
  },
  methods: {
    getUserRepositories () {
      // `this.user` を使用してユーザーのリポジトリを取得します
    }, // 1
    updateFilters () { ... }, // 3
  },
  mounted () {
    this.getUserRepositories() // 1
  }
}

Composition API

次にComposition APIです。
基本的な機能はVue 2を継承している部分もありますが、根本的な記述方法が異なる部分があります。
上記のOptions APIソースを書き換えた場合と比較してみます。

Composition API
// src/components/UserRepositories.vue

import { defineComponent, reactive, computed, watch, toRefs } from 'vue';

export default defineComponent({
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup(_, context) {
    const state = reactive({
      repositories: [], // 1
      filters: { ... }, // 3
      searchQuery: '' // 2
    });

    // computed
    const filteredRepositories = computed(() => {
      ...
    }; // 3
    const repositoriesMatchingSearchQuery = computed(() => {
      ...
    }; // 2

    // watch: プリミティブ型のrefの場合
    const user = ref(0);
    watch(user, (next, prev) => {
      //
    });

    // methods
    function getUserRepositories () {
      // `this.user` を使用してユーザーのリポジトリを取得します
    } // 1
    function updateFilters () { ... } // 3

    // computed, methodsを追記し、使用可能に
    return Object.assign(
      toRefs(state),
      filteredRepositories,
      repositoriesMatchingSearchQuery,

      getUserRepositories,
      updateFilters,

      context
    );

  },
};

全体的に変わった箇所は以下になります。

  • ライブラリインポート
    • 宣言なしで支えていたプロパティをimportする必要があります。
    • 今回の例では、 computed, watchが該当
  • コンポーネント定義
    • export defaultとしていた部分は、 defineComponent ライブラリを使って宣言する形になります。(インポート必要)
  • components, props
    • 記法において基本的な構成は変わりません。
  • プロパティ定義
    • 個人的に一番変わった部分と感じている部分ですが、 setup() 関数内にプロパティ類をまとめる形となっています。
    • また、定義したとしてもObject.assignで宣言する必要があり、こちらで記載していないものは機能できなくなります。
      • data: state に置き換わります。
      • computed: const + computed()関数
      • watch: 監視対象(プリミティブ型や配列等)の種類によって記述方法を変える必要があります。
      • methods: 従来のJS/TSと同じく functionで宣言します。(async等も同様)

その他、根本的な仕様変更としては次のようなものがあります。
・data参照
this.data1 → state.data1
・nextTick: Vueライブラリでインポートが必要
this.$nextTick() → nextTick()
・auth: AuthPluginプラグインが必要
this.$auth.user → authAPI.user
といったように、全体的に this 関数の使用が不要となりました。

全体的な使用感

プロジェクトでは新旧入り混じっているソースでも破壊的なエラーが出ているわけではないのでフラットに使える部分もあるようですが、これまでデフォルトで参照できていたプロパティやライブラリがインポートしないと使えないようになっているという印象が強いです。

  • コードのシンプルさ
  • TypeScriptの全面サポート
  • 開発速度・実行速度もUP
    などが挙げられるVue3のメリットを現時点では実感できていませんが、改めて学習を続け慣れていけたらと思います。

参考文献

Discussion