SMARTCAMP Engineer Blog

スマートキャンプ株式会社(SMARTCAMP Co., Ltd.)のエンジニアブログです。業務で取り入れた新しい技術や試行錯誤を知見として共有していきます。

Vue3にアップグレードしてフロントエンドを改善した話

vue3-migration-improve-frontend

はじめまして! BALES CLOUDエンジニアのえーす(井上)です。この度、BALES CLOUDで長年使ってきたVue2から卒業し、Vue3を導入した状態でリリースできました。今日はこれについてお話できればと思います。

やったこと

  • Vue2を卒業し、Vue3を導入
  • 依存している各ライブラリのバージョンアップグレード
  • 適宜コードの書き直し

Vue3導入と書きましたが、より詳細にはマイグレーションビルドを使ってのVue3導入ができている状態です。つまりマイグレーションビルドによってVue2とVue3が共存しているような状態です(Vueのバージョンとしては3系です)。まだ完全にVue3へ移行できたわけではないのでやることは残っていますが、現状までに多くの知見を得られたので記事としてまとめることにしました。

なぜVue3移行をしたか

BALES CLOUDではVueを使っています。バージョンは2.6.10となっていて、このバージョンは4年前から更新されていませんでした。4年前というと体感的にも相当古いと思います。

古いとはいえ動いているならそのままでステイするのもまた正義だとは思うのですが、BALES CLOUDではVue3移行を決断しました。

以下、理由です。

TypeScriptサポート

フレームワークとしてVue2を使い続けていることによって、やはり辛いと思う部分が多くなってきました。その最大のものが「型」です。

やはりVue3といえばより良いTypeScriptサポートがされるようになった点が非常に大きな進歩だと思います。

Vue2でもVue.extendを使ってTypeScriptを利用できますが、型推論がうまく動かなくて明示的に指定する必要があったり、Vueインスタンスの型があまり使えなかったりしていました。「ああ、Vue2とTypeScirptの相性って微妙なんだなあ」と思うことが多く、積極的に型を使おうと言う気になれませんでした。 そのせいもあり、BALES CLOUDのVueコンポーネントでも、全体として型があまり使われていませんでした。

もっと型を活用しようと考えたのは、フロントエンドでバグがよく発生していた時期があったからです。型がないから起きたバグというわけではないのですが、むしろバグの原因となったものから短期的な改善を目指すのではなく、長期的に品質を上げ、結果として効率よく開発ができる状態を目指すことにしました。そこから型を活用しようという考えに至り、そのための環境があるVue3へ移行することにしました。

このあたり短期的な視点でなく長期的な視点で改善することを決断できるのがBALES CLOUDチームの良いところかなと思います。

各ライブラリが古い

Vue以外にも、Vueに依存する各ライブラリも非常に古い状態でした。例えばElementUI(UIライブラリ)、vue-i18nなどコンポーネントで使っているものや、vue-cliやsass-loaderなどのdevDependenciesなどです。

ライブラリを古いまま放置しておくと、そのうちEOLとなってセキュリティリスクにもなったりしますし、開発者体験としてもよくないので、刷新をしたいと考えました。

Vue2のEOLが近い

皆さんVue2のEOL (End of Life) が今年末までであることはご存じでしたでしょうか。EOLとなるからといって、セキュリティのサポートやブラウザの互換性サポートは引き続きされるようですので、Vue2でステイするという判断も妥当だと思います。が、それも遅かれ早かれ終了するとは思うので、この機会に移行することにしました。

具体的なVue3移行ステップ

以下が具体的なVue3移行ステップです。

  1. Vuetify卒業
  2. Vue3導入
  3. Vue3完全移行

一つずつ詳しく説明します。

Vuetify卒業

当時、BALES CLOUDではUIフレームワークを2種類利用していました。ElementUIとVuetifyです。

Vue3移行するにあたって最大の問題となったのがVuetifyでした。現在はVuetifyはVue3に対応しておりますが、移行を決めた当時はVue3にまだ対応しておらず、Vue3へ移行するのに障害となっていました。

ElementUIについてはVue3対応版としてElementPlusがリリースされていました。かつ、BALES CLOUD全体を見たときに、Vuetifyを利用している割合がElementUIに比べて低かったということもあって、Vuetifyを使うのを辞めてElementPlusに一本化することに決めました。

ElementPlusに一本化する前段のプロセスとして、まずVuetifyを依存ライブラリから抜いて、ElementUIに統一したものをリリースすることで、のちのVue3導入での変更量を減らすようにしました。

Vue3導入

Vue3導入フェーズでは、ゴールを下記に定めました。

  • Vueのバージョンが3系であること
  • vue-compat(マイグレーションビルド)を利用して、Vue2の機能も使える状態であること

つまり、マイグレーションビルドを使った状態でのリリースを目標としました。

これはVue3で使えなくなる機能まですべて修正するより、まずマイグレーションビルドを使った状態(つまりVue2の機能もある程度使える状態)でリリースすることで、コード修正量を減らし、ビッグバンリリースとなることを避けるためです。

こうすることで、このフェーズでやるべきことは下記に絞ることができました。

Vue3完全移行

ここでのゴールは下記のとおりです。

  • Vueのバージョンが3系であること
  • マイグレーションビルドが削除されていること

主にやることは下記の通りです。

  • マイグレーションビルドが出しているWarningへの対処
  • マイグレーションビルドを抜いたことによって動かなくなる部分への対処

ここまでやって初めて「Vue3へ移行した」ことが言えると思います。

ちなみに「マイグレーションビルドですでにVue3にバージョンアップしているのだから、無理に抜く必要なくね?」と思いましたが、マイグレーションビルドは将来のマイナーバージョンでリリースされなくなるので、やっぱり必要です。

移行にあたって問題だったこと

ライブラリのアップグレード

当然ですが、Vueに依存するライブラリはほとんどすべてアップグレードしました。以下、一部ですが紹介します。実際にはもっとあります。

  • @vue-compatの導入
    • マイグレーションビルド
  • element-plusの導入
    • element-uiはVue2までのため
  • vue-i18nを9系にアップグレード
  • vuexを4系に
  • vue-routerを4系に
  • @vue/cli-serviceを5系に
  • sass-loaderを10系に
  • core-jsを3系に
  • @vue/cli~をすべて5系に
    • vue/cli-serviceのアップグレード(webpack5)に合わせるため
  • @vue/compiler-sfcの導入
    • @vue-compatのため
  • node-polyfill-webpack-pluginの導入
    • webpack5系がnodeのpolyfillを自前でやってくれなくなったので

devDependenciesはとりあえず動いていればよいだろうと判断しました。その他は一つずつ公式のBreaking Changesを読み解き、自プロダクトのコードが移行対象でないかを確認しました。Breaking Changesに未記載だったり、単純に確認漏れもあるので、なかなか一筋縄ではいきませんでした。

Vuetify卒業

前述の通りBALES CLOUDはElementUIとVuetifyの両方に依存していますが、このうちVuetifyを抜くとなるとシステムのUIが一部変わることにならざるを得ません。例えば一番顕著だったのがページネーションのデザインの変更でした。こちらは常にユーザーに見えているものなので、デザイン変更の影響が大きいです。

このようなデザイン変更に伴ってPdMを合意を取るべく、変更予定のコンポーネントを一つずつ洗い出して、Figmaに起こしました。それを確認してもらいつつ、かつ実際の環境でも触れるようにして、PdMとデザイン変更の合意形成を行いました。

一つずつUIの対照リストを作成

ElementUI -> ElementPlus

ElementUIからElementPlusへの移行は、一言でいうと苦労の連続でした。おそらくここがVue3移行において一番つらかったと思います。

まずBreaking Changesにも載っていないような本当に些細な変更がところどころあったことが大きかったです。例えばDatePickerなどでのBlurイベントで渡ってくるものがコンポーネントインスタンスからFocusEventになっていたりなどです。もともとこの動きに依存していたので結構困りました。

このような些細な変更に対しては、一つずつコンポーネントを見て動きをチェックする他ないです。かつ問題があったとしても元の仕様を再現するには非常に難しくなっているものも多く、かなり工数がかかりました。

地味にウッとなるのがGitHubのissueなどを漁っていると高頻度で中国語が出てくることです。Elementに中国企業のスポンサーが入っていることが大きいと思います。ページ翻訳すればいいと思うのですが、やっぱりさっと英語で読めるのと比べると少し手間だったり、開発者としても疎外感のようなものを覚えますね。

ElementPlusのissueは半分ぐらい中国語で記載されている

巨大PRによるレビュー負荷

この手の移行はインクリメントとしてリリースできないので、どうしても巨大なPRとならざるを得ません。巨大なPRはそれだけでリスクですし、そもそもエンジニアのレビュー負担も大きいです。

レビュー負荷を下げるべく行ったのは、まずPRの分割です。出てきたバグに対して一つずつアイテム化し、それに対してトピックブランチに向けてPRを作成しました。これによって変更内容と変更理由が一つずつ明確になります。特に移行による変更は複線的なので、一つのPRでまとめてしまうと、どの変更がどのような意図で行われているのか全体像が掴みづらいところがあります。修正を分割することで、そのあたりの負荷を減らすことができたと思います。

チーム体制

Vue3移行は、通常の機能開発をストップせずに行いました。プロダクトロードマップとして達成するべき機能開発があるなかで、開発改善に全リソースを割くだけの余力はないからです。

そのためチームの体制をあらためて考える必要がありました。そして最終的にVue3移行の実行責任者として私が就き、主にVue3移行の主開発やその他開発体制を整える仕事(各ドキュメントやマイルストーンの作成など)を行いました。

このような体制にしたことで、責任範囲が明確になり、自分としては動きやすかったように思います。プロジェクトをドライブする人が一人いることで、「いつまで経っても移行が進まない」みたいな状態を作らなかった点も良かったかなと思います(規模が大きめの開発改善だとあるあるですよね)。

マイグレーションビルドと他ライブラリの相性

マイグレーションビルドは便利なのですが、これをそのまま他のライブラリのVue3対応版で動かすとちゃんと動かないケースが何回かありました。

例えばElementPlusはマイグレーションビルド下だとDatePickerなどの日時入力系コンポーネントにvalueが入らない問題がありました。

最終的な原因としては、Vue3のAPIを利用しているのにマイグレーションビルドによってVue2でコンパイルされていたことでした。これはcompatConfigというオプションをDatePicker系が依存しているElementPlusのコンポーネントに指定することで解決しました。

import { CommonPicker } from 'element-plus'
CommonPicker.compatConfig = { MODE: 3 }

マイグレーションビルドを経由せず一気に移行すれば起きなかった問題ではありますが、今回の場合は仕方ないかなと思います。ちなみにマイグレーションビルド + ElementUIで動かないかとも探ったのですが、それはそれでエラーが出て全く動かず、丸一日試して駄目だったので素直にElementPlusに移行することにしました。

よかったこと

Vue3を導入してみて、あらためて実感したのが型サポートの充実です。

もともと「VueとTypeScriptの相性微妙じゃね?」みたいな空気がVue2では漂っていたと思うのですが、アップグレードして実際にVue3を使ってみると、VueとTypeScriptの相性が微妙という感想がなくなりました。コンポーネントが持っているpropsに対する型も効きますし、綺麗に型推論が使えていたり、thisの型が充実していたり、とにかく型をフルに使える環境が整っています。

充実のthis

これによって、より安全に、より高速に開発できる環境が整ったように思います。この環境だけでも導入した価値があるように思います。

課題感

BALES CLOUDはElementPlusに大きく依存したUIなのですが、Vue3移行をしていて、あらためて「依存していること」自体に課題感を覚えました。

BALES CLOUDはその特徴として第一に「UIが使いやすい」ことを挙げています。これは顧客からもよくフィードバックをいただきますし、開発チームとしても非常に大事にしている価値です。

そのようなプロダクトの価値の根幹を成すUIが、完全にライブラリに依存していることで、移行時に不都合がありました。例えば「ElementUIからElementPlus移行すると、ElementPlusの仕様上、元の挙動にはどうしてもできない」というような事象がありました。

一時はリポジトリをフォークしてくるというようなアイデアも出たのですが、それはそれでアップグレード時に手間だったりして大変だという話にもなりました。最終的には、顧客の体験を大きく損なうことはないと判断し、仕様を少し変えて提供するという形になりました。

ですがこれからも同じような事象が発生する可能性があることを考えると、「UIが使いやすい」ということを標榜するプロダクトである以上、いずれ向き合う必要がある課題ではないかとBALES CLOUDチームでは考えています。

これから

今の状態としては上記で言うところの「Vue3導入(マイグレーションビルド込みでのリリース)」が終わっている状態で、まだマイグレーションビルドからの卒業ができていません。このあたりはこれからまだBALES CLOUDでやっていくところだと思います。ただVue3へのアップグレード自体は終えられたので、大きな山は超えたと言えると思います。

ここからマイグレーションビルドを抜いてVue3に完全対応するまで、BALES CLOUDチームは走っていきたいと思います。

2024/04/03 編集部追記

本記事の続きであるマイグレーションビルドからの卒業についての記事が公開されています。

tech.smartcamp.co.jp