Vue 3に向けてUIコンポーネントのマイグレーションの検証をしました

ビザスクlite事業部フロントエンドエンジニアの小柳(@mascii_k)です。

弊社ではモダンなフロントエンド開発手法にシフトしつつあり、Figma で構築したデザインシステム上でデザインし、それに対応するものを Vue.js 2.x 系で実装する開発がスタンダードとなっております。
現在 Vue.js 3.0.0 の正式リリースが間近とされていて、弊社としても積極的にマイグレーションをしていきたいと思っております。

今回は Vue 2.x 系向けに作られた UI コンポーネントの Vue 3.0 系へのマイグレーションに向けた検証結果を公開します。

弊社の UI コンポーネント事情

弊社では Vuetify や Element UI といったコンポーネントフレームワークには依存せず、極力スクラッチで UI コンポーネントを作って開発しています。

Figma を活用して管理されているデザインシステム

しかし、カレンダーから日付を選択するコンポーネントは複雑なため、例外的に vuejs-datepicker (npm) を活用しています。

検証

弊社で内製している UI コンポーネント群は単純なものが多く、Vue 3.0 系へ容易にマイグレーションできると考えています。
そのため、適度な複雑さがあり弊社でも活用している vuejs-datepicker を Vue 3.0 系へマイグレーションする検証をしてみることにしました。

検証に用いた環境は Vue CLI で作成しました。環境構築の手順は私が Qiita にて公開している Vue 3.0.0-betaのお試し環境をVue CLIで作ってみた を参照ください。

vue ファイルからビルドし直しになった

通常は import Datepicker from 'vuejs-datepicker'; のようにしてビルド済みのファイルを import しますが、Vue 3.0.0 beta ではブラウザでの実行時にエラーが出てしまいました。

このエラーは vuejs-datepicker が vue-template-compiler の 2.6 系を用いてビルドされているので、 Vue 3.0 系と互換性がないため発生したと思われます。

そのため import Datepicker from 'vuejs-datepicker/src/components/Datepicker.vue'; のようにしてビルド前の vue ファイルを import するように変更しました。
変更後、vuejs-datepicker 側で利用している Stylus の導入が必要となりました(なお、弊社では Sass を採用しています)。

prop のデフォルト値が反映されない問題が発生

import を変更することでいったんビルドは通るようになりました。
しかし、 PickerDay.vue というコンポーネントにおいてブラウザでの実行時にエラーが出てしまいました。

エラーを追っていくと PickerDay.vue の dayCellContent という prop 周りで問題が起きていました。

// PickerDay.vue

export default {
  props: {
    dayCellContent: {
      type: Function,
      default: day => day.date // <- ここのデフォルト値が効いてないらしい
    },
  }
}

親コンポーネントである Datepicker.vue にも dayCellContent という prop があって、これをそのまま PickerDay.vue 側に渡しているようでした。
Datepicker.vue 側にデフォルト値の設定がなかったため、エラーが出たものと思われます。

対応方法は2通りありました:

  • 対応1: Datepicker.vue の prop dayCellContent にデフォルト値(day => day.date)を設定する
  • 対応2: <Datepicker> に :dayCellContent="day => day.date" を設定する
<!-- 対応2の例; App.vue -->

<template>
  <Datepicker :dayCellContent="day => day.date" />
</template>

<script lang="ts">
import { defineComponent } from 'vue';

import Datepicker from 'vuejs-datepicker/src/components/Datepicker.vue';

export default defineComponent({
  name: 'App',
  components: {
    Datepicker,
  },
});
</script>

カスタムコンポーネントの v-model の仕様変更

prop の問題は解決できましたが、今度は <Datepicker> で v-model を付けても値が反映されない現象が発生しました。

<!-- App.vue -->

<template>
  <Datepicker v-model="date" :dayCellContent="day => day.date" />
</template>

この現象がきっかけで、カスタムコンポーネントにおける v-model の仕様が変更となっていたことに気付きました。
(RFC: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0011-v-model-api-change.md)

対応方法は2通りありました:

  • 対応1: 利用側で v-model -> v-model:value とし、Datepicker.vue 側で input ではなく update:value を emit するように修正する
  • 対応2: v-model="date" をやめて :value="date" と @input="val => date = val" に分解する

以上で vuejs-datepicker を Vue 3.0.0 beta で利用することが可能となりました。

まとめ

今回の検証で発生した prop の問題以外は事前に予測可能なもので、Vue 3.0 系へのマイグレーションに必要な変更はそれほど多くない印象でした。
また、この検証がきっかけでカスタムコンポーネントにおける v-model の仕様が変更があることに気付けて良かったです。

結論、外部で公開されている UI コンポーネントへの依存が少ないほど、 Vue 3.0 系へのマイグレーションは容易になるのではないかと思いました。
(この記事を執筆中、Vue.js コアチームメンバーのkazuponさんもほぼ同じ結論をつぶやいていることに気づきました)

一緒に働くエンジニアを募集中!

ビザスクに少しでも興味のあるWebエンジニアの方がいたら、ぜひお話を聞かせてください!詳しい募集要項は下記リンクからアクセスしてください。

エンジニアを募集しています

ビザスクでは、エンジニアとして働きたい方を募集しています。
ご興味のある方は下記よりお気軽にご連絡ください。