Vue2→Vue3に移行した話
株式会社ユニラボPRONI株式会社でエンジニアをしているokuyamaです。主にフロントエンドを担当しております。
自社サービスのアイミツで利用しているVue.jsをver.2からver.3に移行したのでその話を書こうと思います。
(執筆してから内容に納得がいかず、また需要もない記事と思い8ヶ月あまり下書きのままお蔵入りにしておりましたが、弊社に興味を持っていただいた方や自身の活動の記録として公開することにしました。執筆から社名が変わっておりますがそれはそれで面白いかな〜と思い修正ではなく取り消し線にて編集を行なっております)
移行対象プロダクト
弊社のサービスサイト、アイミツのフロントはLaravelのbladeテンプレートを使っていますが、一部のUIコンポーネントをVueで作っています。
また、アイミツに登録してくれたお客様が使うマイページと弊社の人間が使う管理画面はSPAで作られており、こちらもVueを使用していますので下記が移行対象のプロダクトになります。
アイミツサイトの一部コンポーネント
マイページ全ての画面
管理画面全ての画面
移行したもの
Vue2からVue3といっても移行するものはそれだけではありません。VueはフロントエンドのフレームワークなのでVueを土台としているパッケージ・ライブラリも軒並み移行が必要になります。
Vue2 → Vue3
PrimeVue2 → PrimeVue3
BootstrapVue → 廃止(PrimeVue3のコンポーネントで書き換え
VueRouter3 → VueRouter4
Vuex → Pinia
Laravel-mix(webpack) → Vite
vue-js-toggle-button → 廃止(PrimeVueのInputSwitchとCSSで書き換え)
その他のパッケージの置き換え&バージョンアップ
移行方法
Vueには移行ビルドが存在しますがそれは使わずにVue2.6からVue3に一気に書き換えを行いました。
理由としては移行ビルドを経由したとしても書き換え作業は発生し、
2.6から2.7(移行ビルド)へ書き換え → 動作確認 → 2.7から3へ書き換え → 動作確認
という動作確認を複数回実施しなければいけないという工数を惜しんだためです。
また、恥ずかしながら当時のフロントにはテストコードが存在せず、テストコードを書いたとしても習熟度的な観点から動作確認は省けないと考えたため上記対応を取ることにしました。
ここからは前提知識をすっとばして具体的な話を書いていますのでVue3移行を行なった(いる)人意外には少しわかりづらい内容になっているかと思いますがご容赦ください
大変だった点
BootstrapVueからPrimeVueへの置き換え
移行対象プロダクトで紹介したマイページについてはUIコンポーネントライブラリにBootstrapVueを利用して構築していました。が、Vue3移行の計画を立て始めた2022年末ではBootstrapVueはVue3に対応しておらず、コアチームによる対応方針についても不明瞭な状態でした。
また、コアチームの対応方針如何を抜きにしてもアイミツのプロダクトでUIコンポーネントライブラリとして同じ役割をもったパッケージがBootstrapVueとPrimeVueが存在し、バージョンアップの管理や開発時に双方のドキュメントを参照しないといけないなどの管理・開発コストが大きいという問題がありました。
そのため、Vue3対応時に思い切ってBootstrapVueを管理画面で使っていたPrimeVueと合わせるという舵きりを行いました。
しかし、同じようなコンポーネントが揃っているとはいえ、機械的な書き換えができるわけではないので単純に書き換え量にコストがかかった点が大変でした。
VueRouterの挙動
Vueのバージョンアップに伴ってバージョンアップできるパッケージは全てバージョンアップしています。なかでも苦戦をしいられたのがVueRouterのHistryStateの挙動の変化でした。
アイミツのプロダクトではURLクエリ文字列で読み込む情報を変えるようになっており、その際にreplace.Stateを使い手動でURLを書き換えるような実装を行なっていました。
しかし、VueRouterのバージョンアップに伴う仕様の変更でURLクエリ文字列やハッシュがブラウザバックを行った際に欠落してしまうようになりました。さらに、マイグレーションガイドの対応だけではアイミツの仕様を満たすことができなかったためrouter.Pushを行う際にクエリ文字列を任意の形式に変換する処理を噛ませるようにしました。
router.push({
query: convertObjectToQuery(myObject),
});
import { format } from "date-fns";
export const convertObjectToQuery = <T extends { [key: string]: any }>(
obj: T
) => {
const query: { [key: string]: string | string[] } = {};
Object.entries(obj).forEach(([key, value]) => {
if (!value) return;
if (typeof value === "object" && Array.isArray(value) && value.length > 0) {
query[`${key}[]`] = value;
} else if (typeof value === "object" && value instanceof Date) {
query[key] = format(date, "yyyy-MM-dd", { locale: Ja });
} else {
query[key] = value;
}
});
return query;
};
blade(Laravel)との繋ぎ
今回一番難儀したのがこれです。Vue2→Vue3マイグレーションガイドでいうところのMount API Changeです。
Vueのあるべき姿としては至極納得のいく変更ですが、アイミツサイトにおいてはこの変更を素直に受け入れることができませんでした。
サイトは常時複数のA/Bテストを行なっており、エンジニア以外でもA/Bテストのスクリプトやスタイルの変更をする可能性があったためタグの構成を変更することは開発以外での調整コストが高くなりできれば避けて通りたいところでした。
暗中模索の結果、下記のようにタグの構成を変えず無理やり(?)Vueコンポーネントを置き換える手法をとりました。(これが最適解かどうか今でも確信はありません)
※コードはかなり省いていますので雰囲気で読み取っていただければと
const vueElement = document.querySelector(tag);
if (!vueElement) return;
const component = createApp(Component, propObject);
component.mount(tag);
const mountedElements = vueElement.children[0];
if (!mountedElements) return;
vueElement.replaceWith(mountedElements);
移行して良かった点
npm run devが爆速になった
これはVue3というよりはVue3移行に伴って開発系パッケージも更新し、ビルドツールもLaravelMix(WebPack)からViteに変えたことによる恩恵です。
移行前は npm run dev(watch) で2〜3分かかっていたところがコンマ何秒の世界になり開発体験がめちゃくちゃ向上しました。
型安全が強化された
Vue3の恩恵としてよく言われていることですが、例に漏れず我々のプロダクトでも実感が大きかったです。
とくにProps、Emit周りがスッキリして大変扱いやすくなったと思います。
パッケージの更新ができるようになった
PrimeVueをはじめとするパッケージ群がVue2に依存したバージョン、つまり型落ちのバージョンしか使うことができず、さらには一部のパッケージではすでにDeprecatedになっていたり、、という状態から脱却できした。
今後も継続的にパッケージのバージョンアップができる土壌が整ったことがよかったかなと思います。
最後に
拙い文章かつ歯抜けの情報をつらつらと書き綴ってしまいましたがこれからVue3移行する人、今移行している人への一助となれれば幸いです。すでにVue2は2023年末でEOLになっているため活動の記録として公開させていただきました。
【PR】PRONI に興味がある方へ
今回の記事を読んでPRONIに興味を持っていただけた方は、まずはカジュアル面談でざっくりお話させていただければと思います!
【PR】PRONIに関する情報配信登録
PRONIに関する最新情報、イベント情報、採用情報など配信しております。
ご希望の方は以下のフォームよりご登録をお待ちしております!
この記事が気に入ったらサポートをしてみませんか?