- ECのAPI開発におけるPM
- Golangエンジニア
- コンサルティング営業
- Other occupations (1)
- Development
- Business
2022年に新卒入社し、GMOメイクショップ コアグループにてフロントエンドエンジニアをしている原田です。 調査も含めると約半年をかけた管理画面のVue2 / Vuetify2
からVue3 / Vuetify3
への移行が無事完了したので、その移行の軌跡を共有します。
背景と目的
2023/12/31でVue2はサポート終了を迎えました。サポート終了を迎えると、有償延長サポートを使わない限り、バグ修正・セキュリティパッチが一切受けられなくなり脆弱になってしまいます。 それ以外にも Vue3で新たに追加される機能やVueコミュニティで新た開発されるツール類等が利用できず開発者体験も下がったり、古くなるに連れてVue2での仕様調査が難しくなる等の問題が出てきます。 バージョンアップはしなければならないものとして、Vue3/Vuetify3への移行を決行しました。
かかった期間と人数規模
2022年末にVue2環境での前対応、2023年5月頃に本格的にVue3移行の調査を開始し、7月から11月にかけて コアグループ+SREチーム 10名程度、QAチーム 5名程度の 15名規模で移行しました。
移行したプロダクト
通称 次世代ECと呼ばれる ショップ運用者様向けの新管理画面、リリース済みのもの全て一式が対象でした。 管理画面は主に商品/注文/会員等の登録・編集を行うためのもので、現在約20ページほどの複雑な画面で構成されています。
移行前後の比較
若干のデザイン崩れは未だ残っているものの、よく見なければ気づかないほどの出来に移行が完了しました。
- Before
vue2環境でのダッシュボードトップページ
- After
vue3環境でのダッシュボードトップページ
- 移行前後の依存関係比較
移行にあたっての手順
今回の移行は下記の手順で行いました。
- 2022
- Vuetify3を待つ
- Vue2の間にできることをする(約2ヶ月)
- 進め方などの検討
- 2023
- Vue3移行ブランチ環境を整える
- 動作しない箇所を調査・修正 (約3ヶ月)
- デザイン崩れの修正 (約3週間)
- developブランチへのマージ
- デザイン崩れの修正 (約1週間)
- Vue3移行完了・リリース
Vuetify3を待つ (2022/07~)
2022年7月、Vue2.7が Vue2系列の最後のアップデートであり 2023年12月31日でEOLとなることが公表されました。 この時点からVue3への移行を検討していましたが、Vuetify2がVue3では動作しないことから、進められない状況となっていました。 2022年11月になり、ようやくvuetify3がリリースされそこから移行を見据えた動きが開始しました。
Vue2の間にできることをする (2022/11 ~ 2022/12)
Vue3に上げる際には破壊的な変更がかなりあります。 Vue3本対応を始める前段階として、可能な限りのことをこのタイミングで行いました。
vue2.6からvue2.7へのアップデート
Migration to Vue 2.7 — Vue.js の通り 依存関係を更新しました。 リアクティブな変数の名前を _や$で始められなくなる
という仕様変更があったものの、今回は運良く遭遇しなかったためスムーズに完了しました。
CompositionAPI化 (script setup化)
Vue2時点では vue-property-decorator と vue-class-componentを利用していました。 vue-property-decoratorはVue2時代、日本ではかなり利用されていたクラスベース記法ライブラリです。しかしながら、このライブラリは2021年9月以降更新がなく Vue3の対応はRCで止まってしまっていました。この記法を引き継ぐ vue-facing-decoratorもあるものの、Vue3ではVue公式で提供されるCompositionAPIがスタンダードとなっていることから、CompositionAPI化に踏み切りました。vue-property-decoratorからscript setup記法へ直接移行できるツールは当時なかったため、Pythonで正規表現ベースの移行支援スクリプトを作成し半自動で移行しました。
vuexからpiniaへの移行
vuexはVue公式から提供されていたグローバル状態保持のためのライブラリです。Vuexは今のところVue3でも機能するため必須ではありませんでしたがVue2.7環境での開発における、ストア利用をより簡単にするため、型支援強化のため、またVueの推奨ライブラリとなったため、同時に対応しました。
webpackからviteへの移行
viteはwebpackよりも高速なビルドツールです。viteへの移行は必ずしも必須ではありませんが、webpackでのビルドに時間がかかっていたこと、Vue公式Vue3移行ガイドにて推奨されていること、Vitest等のVite前提のツールが増えてきていることからviteへ移行しました。
vue-i18nでの移行ビルド利用
このプロジェクトでは文言修正・統一をしやすくするため、文字列の表示にvue-i18nを利用しています。vue-i18nには vue3への移行を支援するための vue-i18n-bridge というライブラリが提供されていたため、そちらを使うように呼び出し元を置き換えました。
移行の進め方などの検討 (2023/05)
- 別のJS・UIフレームワークへの移行
この段階でSNS等の評判を聞くと、筆者の周囲では そもそも Vue・Vuetify3を離脱するという事例も増えてきていました。しかし、この段階で既にCompositionAPI化をしていたこと、現状のコードがVuetifyコンポーネントを前提としてそれをハックするCSSで書かれていることなどから 1から書き直すことは チーム規模・学習コスト・時間コストを考えると現実的ではなく、Vue/Vuetify3となりました。
- 新プロジェクトとして作り直し移植するか現プロジェクトをバージョンアップするか
新機能を追加するプロジェクトが並行で進んでおり、Vue2でのブランチ作業を完全に停止することはできないため 必然的に現プロジェクトそのままでのアップデートになりました。
- developブランチを分けるかdevelopブランチに直接コミットするか
vue2での開発を止めることは戦略上できないため、ある時点のdevelopブランチ(vue2)からフォークしたvue3ブランチを作成し、定期的にdevelopブランチでの作業をvue3ブランチにマージしながら進みました。vue3で変更のあった箇所は、都度コンフリクトに耐えつつの移行となりました。
- 動作検証方法
弊社ではローカル環境/開発環境/ステージング環境/プロダクション環境の 4つがありましたが、新たに追加でvue3専用の開発環境を用意し、Vue2当時のものと見比べつつ検証を進めることとしました。
移行本番 (2023/06~2023/11)
Vue3の初期化処理変更
- 依存関係の更新 (不要な依存関係削除)
- createAppへの構文変更
- vue/compatの導入
- vue-routerのcomposableインポートパス変更
- vue-i18nのuseI18nインポートパス変更
依存関係の更新 (不要な依存関係を削除)
まずはWebpackで利用して残っていた不要な依存関係を整理し、バージョン互換性を確認するところから行いました。 互換性がないものは一旦コメントアウトし、コア部分が機能するようになってから改めて別のライブラリを入れ直しました。
ESLintルール変更
- eslint-plugin-vueの
plugin:vue/vue-recommended
ルールをplugin:vue/vue3-recommended
ルールに変更 - eslint-plugin-vuetifyの
plugin:vuetify/base
ルールを追加 - エラーになった箇所を全て置き換え修正しました。
トップページを開けるようにする
ここまでで最低限、画面の描画程度は行えるものと考えていましたが、全く画面が表示できませんでした。 まずログイン画面までは表示できなければ次に進めないということで、細かいコンポーネントを全てコメントアウトし、描画ができるところを徐々に増やす修正作業が続きました。 原因を調べると、vue/compatのデフォルト設定では Vuetify3がかなりエラーを吐いてしまうことがわかったため、ほとんどの互換モードを無効にすることとなりました。 結果的にcompatはあまり意味がなかったかもしれません。
Vue 2からVue 3 への移行
これで一旦ログイン画面が出るようになりました。 以降、基本的に 公式サイトに書かれた移行ガイドの記載どおりに進めます。 下記に特にこのプロジェクトで影響があった変更を抜粋します。
一般的な破壊的変更
v-model
の仕様変更- v-bind obj.sync の代わりにv-modelが使われるように。
- デフォルトのv-modelの参照先が prop名 value / emit名 input から prop名 modelValue / emit名 update:modelValueに。
- 元々v-modelをあまり利用していなかったため、使わない方向で修正しました。
.native
修飾子の廃止- click.nativeを利用していた箇所をclickに差し替え修正しました。
特に踏み抜いた変更
- Ref値のstructuredCloneが行えなくなっている
- Ref値の実装方法が変更されており直接structuredCloneできなくなっていました。
- toRawとtoRawDeepを使った方法を利用して回避しました。
- コンポーネントに
false
を渡すと"false"
としてレンダリングされる - 渡されているPropを直接書き換えていた処理でPropの書き換えが機能しなくなる
- Propを書き換えず正しくEmitするように修正しました。
- Vueの操作対象の要素をgetElementByIdで直接操作していた箇所に後者の変更が反映されなくなる
- refsを経由しVueのみで操作するように修正しました。
Vuetify2からVuetify3への移行
同様に 公式サイトに書かれた移行ガイドの記載どおりに進めました。 同じくこのプロジェクトで影響があった変更を抜粋します。
一般的な破壊的変更
- VTooltipの activatorの仕様が変更されている
- ドキュメントを確認し v-on v-bind等を書き換えました。
- ボタン等のデザインを指定するprop群がvariant propにまとめられている
- ドキュメントを確認し 元通りのデザインになるよう variant propを指定し直しました。
v-data-table
が実験段階 かつ かなり仕様が変わっている- v-data-tableに依存するコンポーネントが多数あったため labs段階を許容しました。
- (移行完了直前に 3.4.0で 正式リリースされました)
v-date-picker
が実験段階 かつ 見た目が大幅に変わっている- どうにもならなかったため Vue3DatePickerをベースとして、なるべく見た目が近いコンポーネントを作り直しました。
特に踏み抜いた変更
- デフォルトでバリデーションエラー分の高さが確保され、全体的にマージンがズレている
- hide-details="auto" を付け、エラーが発生しない限りマージンを発生させないようにしました。
- VSelectの 選択変更時にEmitされるオブジェクト型が変更されている
- あまり言及されていませんでしたが、このオブジェクト型の変更でかなりエラーが発生しました。
- VSelectに渡す配列のキーを示すpropの
item-text
とitem-value
が効かない - Prop名が
item-title
とitem-value
に変更されていました。終盤まで気づかず 結局呼び出し元全てを title/valueに変更しました。
- Prop名が
- VSelectのitemsの一部に極端に長いテキストが含まれる場合 激しく幅が変動する
- VOverlayがデフォルトでブラウザの戻る動作を乗っ取る仕様になっている
- このプロジェクトでは全画面共通で1つのv-overlayを操作しており、v-overlayが見えない状態で常に存在していることで ブラウザの遷移動作が何もエラー無く暗黙的に妨げられるようになっていました。
- propを変更し対応しました。
- VDataTableのヘッダースロット仕様が大幅に変わっている
- スタイルを付ける方法についての記載がドキュメントにありませんでした。
- VDataTableの実装コードを見ながら 同じコンポーネントにクラスをつけてslotとして置き換えました
Vuetify3のバグ
- VSelectセレクターを開いた際、選択中の項目位置で開かない
- 調査に時間がかかりましたが vuetify3そのもののバグで 3.4.0で修正されていました。
- VTooltipが稀に残る
- ボタンを押したときに表示中のツールチップの内容を差し替える実装をするとマウスを離しても残るバグがありました。
ライブラリ組み合わせのバグ
- VuetifyコンポーネントをVue3DatePicker内で使うと
[Vuetify] could not find defaults instance
が発生する - Vue3DatePicker側のバグで アップデートで修正されました。
VueRouterの移行
公式サイトの移行ガイドを参考としました。 あまり引っかかる点はありませんでしたが、1点だけ問題が発生しました。
特に踏み抜いた変更
- 予めルート定義に含めていないparamsが渡せないようになっている
- 代替案は ストア/queryパラメータ/HistoryAPIステート/ナビゲーションガードでのmeta書き換え の4択
- ストアに状態を保存し遷移する仕様に変更しました。
Vue-I18nの移行
公式サイト記載の通りでした。 既に移行ビルドを使用していたため、基本的にはインポートパスを差し替えるのみでした。 しかし1点だけ問題があったため共有します。
特に踏み抜いた変更
- グローバル参照に関する問題
- 一部の共通処理定義パッケージにてVueコンテキストの外でvue-i18nを呼んでおり、ライフサイクルの変更で該当箇所が機能しなくなりました。
- 一旦i18n-jsで置き換えましたが互換性の無い機能があったため、useI18n().global を参照し、グローバルインスタンスを参照するようにしました。
デザイン崩れの修正
- 共通化されていなかったスタイルを共通化する
- CSSを!importantで上書きする箇所をなるべく減らす
- sass変数で書き換えできる箇所は sass変数を利用する
- 個別でVuetifyのクラスを上書きし設定されていたフォントサイズを SASS変数に置き換えました。
- vuetifyのユーティリティクラスを活用する
margin-top
/font-weight: bold
等の単純なクラスは Vuetifyのユーティリティクラスに全面的に置き換えました。
- v-deep scoped を活用する
- どこに対してかかっているかわかりづらかったスタイルを 実際の利用コンポーネント内に寄せて整理しました。
移行の感想
よかったこと
- 共通化できるところ・実装上の課題を確認できた
- 各種コンポーネントを見直すこととなり、今後の課題を発見できました。
- ハックしていた書き方がかなり減った
- vuetifyコンポーネントの仕様上直接操作できない要素をdocument.getElementByClassName経由で編集する、propを直接書き換える等のハックな操作は今回の移行で壊滅しました。これにより改めて一般的な実装を検討し、ほぼ書き直すことができました。
よくなかったこと
- 移行ドキュメントをしっかり読まないことで時間がかかっていた
- ほとんどのことは移行ドキュメントに記載されているため、長いですがしっかり読みましょう。
- Vue3移行だけのつもりで、リファクタも混ぜたプルリクエストを何度か出してしまった
- レビューをしやすくするためには、たとえ気になったとしても 1つのプルリクエストに1つの変更を入れるべきでした。これにより、レビューが難しく詰まる場面もありました。
気づけたこと
今後の展望
- Vuetify3のバージョンアップ
- 移行作業中にもVuetify3の更新が進んでしまい、最新版は 3.5.0、VDataTableも正式版となっています。現状Vuetify公式サイトは過去のマイナーバージョンのページを閲覧できないため、記載があっても使えない機能が発生しつつあります。混乱を生んでしまうため、なるべく早く最新版に更新したいです。
- 各種テストツールの導入
- 実のところVue2環境では、フロントエンドのテストツールがほぼ機能していませんでした。改めて設定するにも、Vue3移行時の破壊的な変更が懸念されるため導入に手を出せていませんでした。Vue3移行が終わった今、vitest / playwright / storybook 等フロントエンドの開発ツールを順次導入し、手動テストを少しでも減らしていこうとしています。
終わりに
Vuetify2からのVuetify3への移行は破壊的変更が多くとても壮大な移行プロジェクトとなりましたが、なんとか達成することができました。代表して移行記事を書かせていただきましたが、対応にご尽力頂いた方々ありがとうございました。
参考
資料
Vuetify3 でよく使うコンポーネントのメモ(1) #Vue.js - Qiita
他の方の移行事例
主力製品の Vue 3 & Vuetify 3 へのマイグレーション全記録
Vue3にアップグレードしてフロントエンドを改善した話 - SMARTCAMP Engineer Blog
Vue2系からVue3系への移行における苦労話 - OPTiM TECH BLOG
SoqAlbumを大幅アップデート、vue(tify)3移行への道のり | Yagifulのブログ
Vue2の間にできること参考
Vue 3 / Vuetify 3 に備えてまず Vite への移行をお勧めしたい
Vue3 に向けて - vue-i18n-bridge による移行準備
この記事にご興味のある方ぜひお話しましょう。ご応募お待ちしています!
◆ 他のBlogはこちらから⇒ https://tech.makeshop.co.jp/ ◆