
Vue.js 2系から3系への移行体験記
この記事を書いたきっかけ
周知の通り、Vue.js 2系のサポートが2023年12月31日で終了します。
筆者は2023年8月から9月にかけて、グラフ画像から値を抽出するWeb GUIアプリケーション「StarryDigitizer(※1)」を、Vue.js 2系(以下、Vue 2)から3系(以下、Vue 3)に移行しました。
この時の試行錯誤の履歴を書き残しておくことで、現在移行作業をしている方やこれから移行を検討している方に少しでも役に立つ情報を提供したいと考え、本記事を執筆しています。
「もうサポートが終了するタイミングに執筆するのでは遅いではないか」と怒られそうですが、おそらく(自分含め)いまだ移行できずVue 2のままで運用しているアプリケーションがたくさんあることを期待しています(?)。
要点
本記事で特に伝えたい内容は以下の通りです。正直、ここだけ読んでいただければ以降は長いので読まなくても大丈夫です。笑
Vue公式の移行ガイドとドキュメントを読めば大筋は間違えない
本記事では移行対応において重要と思われる箇所を随所に引用しているので、参考にしていただければ幸いです。
可能であればビルドツールを高速でTypeScriptフレンドリーなViteにしよう
VuetifyなどのUIフレームワークを使っている場合、公式の移行ビルドは成功しない確率が高い
Vuetify2 -> Vuetify3の移行は、eslint-plugin-vuetifyがほぼ必須
Composition APIへの移行は大規模かつ動作のマスト要件ではないので、後回しでもOK
実際の移行対応のPull Request
コミットメッセージなど汚く、お見せするには恥ずかしいのですが、参考になる部分があればと思いますので移行時のPull Requestを共有します。
Webアプリケーションの構成
Vue 3への移行と一口に言っても、移行対象のアプリケーションがどのようなビルドツールで動き、どのようなライブラリに依存しているかによって、具体的な対応は変わってきます。
以下、今回の移行対象である「StarryDigitizer」の簡単な構成です。
StarryDigitizer
ビルドツール
Vue CLI
パッケージ管理
yarn
言語
TypeScript
使用ライブラリ
Vuetify 2
Vuex + vuex-smart-module
テストツール
Jest (Unit Test)
Cypress (E2E Test)
対応方針の検討
当初、移行の方法として大きく2つの候補があげられました。
A: 既存の開発環境のまま、移行ビルド等を活用してVue.jsのバージョンのみを上げる
B: 完全に新規の開発環境をVue 3系前提で立ち上げ、そこにコードを移植していく
結論として、実際の移行はBの新規立ち上げで行いました。しかし、まずはミニマムな対応で移行ができないかの検証として、Aの移行ビルドを試してみることにしました。
公式の移行ビルドにトライ→失敗
Vue.jsは、公式にVue 2からVue 3への移行ガイドを発行しています(このページは、本記事でこのあと何度も参照されます)。
ガイドによれば、移行ビルドと呼ばれる@vue/compatを用いて、段階的にVue 3へのアップグレードができると記載があります。
具体的な移行手順は上記ガイドに掲載されているため、本記事では触れませんが、実際に移行を進めていくと、UIフレームワーク「Vuetify」のバージョン3への移行で行き詰まってしまいました。
一通り必要なバージョンアップを行った後、アプリケーションをビルドしようとすると、どうしてもVuetifyの依存関係が解消できず、ローカルサーバーの立ち上げができなくなりました。
よくよく移行ガイドを読んでみると、以下のように記載があるではないですか。
あなたのプロジェクトが Vuetify, Quasar, ElementUI のようなコンポーネントライブラリーに依存している場合、それらの Vue 3 互換バージョンを待つのが最善です
・・・ドキュメントは最初からしっかり読もう、ですね。
どうにも動作せず、公式もVuetifyに依存したアプリケーションで移行ビルドを推奨していないということで、早々に方針転換し、新規の開発環境を立ち上げることにしました。
実際の移行作業
Viteの導入
まずはビルドツールの選定からです。これまでVue CLIを使用していましたが、公式ガイドでViteが推奨されていたため、ここは素直にViteを採用することにしました。
Vue 3 プロジェクトの新しいビルドツールチェーンとして、Vite を推奨することになりました。Vite は、非常に高速なサーバー起動とホットアップデートのパフォーマンスを提供する新しいビルドツールです。元々は Vue チームによって作られたものですが、現在はクロスフレームワークのツールとなっています。詳細は Vite を推奨する理由をご覧ください。
Viteが推奨される理由は、一言でいえば「開発サーバーの起動および更新が高速にできるから」のようです(実際、Vue CLIのローカルサーバー起動が10秒以上かかっていたのに対し、Viteでは2秒ほどで起動できるようになりました)。
また、公式ドキュメントには、TypeScriptの型チェックの観点でもViteを強く推奨するという記載があります。
現在、Vue CLI 経由で Vue 3 + TypeScript を使っている場合、Vite への移行を強く推奨します。トランスパイルのみでの TS サポートを有効にする CLI オプションにも取り組んでいるので、型チェックのために vue-tsc に切り替えることができます。
これらのことから、TypeScriptでVue 3の開発をするプロジェクトにおいては、Viteを選択するのが賢明な判断と言えると思います。
余談ですが、Viteの読み方は「ヴァイト」ではなく「ヴィート」らしいです。筆者はいまだに間違えます。
コードの移植
Viteの初期設定が完了したら、従来のアプリケーションコードを移植していく作業に移ります。
ここで、StarryDigitizer創始者・頼れるエンジニア 間藤 智也氏が、ドメイン駆動設計に基づいてコアロジックを分離してくれていたのが非常にありがたかったです。
コアロジックがVue.jsのバージョンに全く依存していないため、移行時に考慮が必要なコードの量が少なく、スムーズに作業ができました。
ドメイン駆動設計、とてもオススメです。
vuex-smart-moduleからPiniaへの移行
StarryDigitizerでは従来、vuex-smart-moduleを使ってストアの実装をしていました。これがVue 3に互換性がないので、このタイミングで移行対応を行います。
代替として、Vuexを素のまま使うことを検討していたのですが、どうやらPiniaという聞き慣れないストア管理ライブラリが、Vue.jsの公式で推奨されるようになったことがわかりました。
調べてみると、大幅なコードの改修をすることなく(少しは必要)Vuexからの移行ができるようだったので、これも素直にPiniaを採用することにしました。
特に(非推奨ではあるものの)移行への救済策として用意されているmapState, mapActionsを用いることで、ミニマムの改修で一旦アプリケーションを動かすことができ、とても助かりました。
ESLintの設定 - Vue.js編 -
現行で動かしている具体的なESlintの設定はこちらのファイルをご覧ください。
まずはVue 3の破壊的変更に対応できていないコードがないかチェックが必要です。
eslint-plugin-vueを導入し、自動修正の設定をしてやることで、Vue 3に互換性のないほとんどのコードを自動で互換性のあるコードに修正してくれます。
Visual Studio Codeを使っている場合、.vscode/settings.jsonにはこのように記載する必要があります。
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
肝要なのはformatOnSaveをfalseにしておくことで、これがtrueだとeslintの自動修正とかち合ってしまい、予期せぬ挙動を引き起こす原因になります。
StarryDigitizerでは、Vue 3に互換性のないコードはほとんど見つかりませんでした。
ESLintの設定 - Vuetify編 -
次に、Vuetify2からVuetify3への移行で動かなくなるコードの検証です。
Vuetify3は、Vue 3とは比べ物にならないくらい破壊的変更が多く、それらを1つ1つ目視・手作業で修正していくのは不可能に近いです。
しかし、 eslint-plugin-vuetifyを使うことで、前述の自動修正機能を通じて、ほとんどのコードをVuetify3に互換性のあるものに書き換えをしてくれます。ありがたや。。。。
eslint-plugin-vuetifyの設定は、Vuetify3への移行でほとんど必須といっていいプラグインだと思います。
手作業で修正したこと
ESLintで自動修正できず、手作業で修正した部分がいくつかあるので書き留めておきます。いずれも大して手間のかかるものではありませんでした。
Vue.extendからdefineComponentへの書き換え
//Before
import Vue from 'vue'
export default Vue.extend({ //コンポーネントのロジック })
//After
import { defineComponent } from "vue";
export default defineComponent({
1つの単語で命名されたコンポーネントを2単語以上に
Vue.jsのスタイルガイドに反するとしてeslintによりエラー表示されるため(vue/multi-word-component-names)
Vuetifyのスタイル崩れ
Vuetify3に移行したことで一部スタイルの崩れが発生。タグのprops等調整して対応。これが一番面倒でした。。。
その他、細かいTSエラーの対応やテストツールの移行も手動で行いましたが、どれも大した工数はかかりませんでした。
調査期間を除き、実際に移行プロセスを開始してから、3日ほどの実働で移行が完了したように思います。
残作業:Composition APIへの移行
今回の移行作業で、Vue 3の要の機能のひとつであるComposition APIへの移行は意図的に後回しにしました。
理由は、今回の移行の要件はあくまで「既存機能がVue 3で動作すること」であり、Composition APIへの移行は動作に必要な要件ではなかったためです。
また、Composition APIへの移行はコンポーネントを包括的に書き換える必要があり、大規模な改修となるため、現状も新規開発を優先し、Vue 2からの機能であるOptions APIを用いています。
しかしながら、早めにComposition APIへ移行した方がVue 3の恩恵を最大限に享受できるのは間違いないため、徐々にでも移行作業を進めたいと考えています。
移行を経験しての所感
今回、筆者自身にとっては初のVue 2 -> Vue 3への移行対応でしたが、試行錯誤はしたものの、最終的には綺麗な形で移行できたのではないかと考えています。
Vue 2のサポート終了やVue 3の破壊的変更はしばしば批判的に書かれていますが、個人的には移行がものすごく大変だったという感覚はありませんでした。もっとも、数百コンポーネント単位の大規模アプリケーションともなれば話は違うのでしょうが、、、
Vue自体のアップデート以上に苦しめられたのが、UIフレームワーク Vuetifyの移行でした。eslint-plugin-vuetifyがなければほぼ詰んでいたと思います。
また、Vuetify 2とVuetify 3では各パーツのデザインも微妙に変わっており、結果StarryDigitizerについても移行前と移行後で見た目を完全に揃えることは諦めました。
劇的にコーディング工数を削減してくれるUIフレームワークですが、こうした依存の特性を理解した上で有効活用することが重要だと実感しました。
Vue.jsを今後も使い続けるのか?
巷ではもっぱらReactを選定したという話ばかり聞くようになり、以前に比べるとVueの人気は影を潜めてしまったようにも感じます。
確かに開発規模、コミュニティの規模を考えれば、Reactが優位に立つというのも頷けます。
しかしながら筆者個人としては、Vueの直感的でHTML + JSの上位互換のような使い勝手の良さが気に入っていますし、TypeScriptについてもしっかりと設定してやることで快適に扱えるようになってきていると思います。(工夫すればドメイン駆動設計ともちゃんと共存できます)
かといって、Vueだけにとらわれるのも良くないので、当面の間はReactとVue、それぞれアプリケーションの規模や内容に応じて、適した方を選定して使い分けてみようと思っています。
内心では、Vue Loverとして、人気復活を密かに願っています。笑
本記事は以上です。長々と綴りましたが、ここまでお読みいただいた方は本当にありがとうございました。読んでくださった方のVue開発ライフが、今後とも充実したものでありますように。
注釈
※1 StarryDigitizerはオープンソース化されており、Publicなgitリポジトリで管理されている。現在も活発に新機能開発やリファクタリングが行われている。