STORES フロントエンドエンジニアの wattanx です。
2022/11/16、ついに Nuxt 3 が正式リリースされましたね。
私たちのプロダクトである STORES でも Nuxt を利用しており、絶賛 Nuxt 3 への移行中です。
本記事では、私たちのプロダクトにおいてどのようにNuxt 3 に移行しようとしているのか紹介します。
また、これは STORES Advent Calendar 2022 の 11日目の記事です。
移行の基本方針
Nuxt 2 を利用しているプロダクトを Nuxt 3 に移行する場合、以下の2とおりの方法が一般的だと思っています。
- Nuxt 2 から Nuxt 3 に直接移行する。
- Nuxt 2 から Nuxt Bridge に移行する。その後 Nuxt Bridge から Nuxt 3 に移行する。
Nuxt Bridge とは、かんたんにいうと Nuxt 3 の機能の一部を Nuxt 2 でも利用できるようにしたライブラリです。
Nuxt Bridge を使用すると、既存の Nuxt 2 のプロジェクトを Nuxt 3 にほぼ対応させることが可能です。
私たちのプロダクトでは後者の Nuxt 2 から Nuxt Bridge に移行する。その後 Nuxt Bridge から Nuxt 3 に移行する。
方法で移行を進めることにしました。
Nuxt Bridge を経由する理由としては、下記のとおりです。
同じリポジトリ内で移行しやすく、高速化等の恩恵を受けられる
移行は基本的に移行ブランチを作成して移行していく予定で考えています。
移行中も続々と新機能や施策が追加されるため、移行ブランチの存続時間が長くなるとそれらの変更を移行ブランチにマージしていくのは大変だと考えていました。
Nuxt Bridge を経由する場合だと、Nuxt2 から Nuxt Bridge への移行
Nuxt Bridge から Nuxt 3 への移行
と2つの移行作業が発生します。
しかし、それぞれの移行ブランチの存続時間は Nuxt 3 に直接移行するよりも短いだろうと考えています。
Vue 3 に直接移行するのは難易度が高いため
Nuxt 3 へ移行するためには、Vue 3 への移行も必要です。
私たちのプロダクトでは、Nuxt 3 への移行
と Vue 3 への移行
をまとめて対応するのは難しいと判断しました。
Nuxt Bridge を使えば Vue 2.7 が使用でき、Vue 2.7 と Nuxt Bridge から Nuxt 3 と Vue 3 への移行のほうが対応しやすいと考えています。
Nuxt Bridgeで動かない Nuxt Modules は剥がして自分達で作れる
一方で Nuxt Bridge への移行が避けられる理由として、Nuxt 3 に比べて Nuxt Bridge では対応している Nuxt Modules が少ないという問題があります。
ただし、私たちのプロダクトで使っている Nuxt Modules は公式の Nuxt Modules を使わず自分達で実装できると判断しました。
どのように移行していくのか
Nuxt 3 へ移行するにあたって、Vue 2系から Vue 3 に移行する必要があります。
また、Nuxt Bridge に移行するにあたって、Vue 2.7 への対応も必要です。
なので、私たちは下記のようなタスクに分けて進めています。
- Vue 2.7、Vue 3、Nuxt Bridge にバージョンアップしなくても対応可能なタスク。
- Vue 2.7 にバージョンアップしないと対応できないタスク。
- Vue 2.7 にあげることでほぼ Vue 3 に対応できる。
- Nuxt Bridge にバージョンアップしないと対応できないタスク。
- Nuxt Bridge にあげることでほぼ Nuxt 3 に対応できる。
どのように進めているのか
Nuxt 3 への移行は進めたいが、プロダクトの機能開発を止めるわけにはいきません。
私たちのプロダクトでは、プロダクト開発を止めずに Nuxt 3 への移行を進められるように Nuxt 3 WG(ワーキンググループ)を立ち上げて、各調査や意思決定を行っています。
(フロントエンドのメンバーは9人でそのうち WG のメンバーは4人)
各種調査の結果、フロントエンドのチームメンバー全員で対応したほうがいい場合は移行手順をまとめて、Nuxt 3 WG 以外も含めてチームメンバー全員で対応しています。
WGが実際に行った作業
WG が実際に行った作業のうち一部を抜粋して紹介します。
bridge: false の状態でNuxt Bridge を導入する
まずは Nuxt Bridge を入れてみて、どのくらい変更が必要なのか検証してみました。
すると、下記のような課題が出てきました。
- Nuxt Bridge を入れると、Vue 2.7 が入る。
- Vue 2.7 にバージョンアップしないと、Nuxt Bridge に移行できない。
- Vue 2.7 への移行もすぐにできない。
そのため、下記の方針でリリースしました。(特に問題は出ていません。)
bridge: false
の状態で Nuxt Bridge を導入する。- package.jsonの
resolutions
を使ってVue を2.6系に固定する。
Vitest への移行
私たちのプロダクトでは、テスティングフレームワークに Jest を利用しています。
Vue 2.7 へバージョンアップすると、テストが軒並み通らないということが判明しました。
時間を書けて調査した結果、原因がわからない状態でしたが Vitest に移行すると解決することがわかりました。
これ以上調査にコストをかけるよりも
- Nuxt Bridge や Nuxt 3 では Vite を利用していきたいと思っていた。
- 社内の他のプロダクトでも利用しており、移行したいモチベーションがあった。
- @nuxt/test-utilsは Vitest と Jest の両方をサポートしているため。(バージョン
3.0.0
以降)
という理由から、この機会に Jest から Vitest へ移行することにしました。
現在、半分以上は Vitest に移行完了しています。
Vue.extend を defineComponent に置き換える
Vueファイルで TypeScript を利用する場合、以前はVue.extend
を使った方法が主流でした。
しかし、Vue.extend
は Vue 3 ではなくなるので、defineComponent
に移行しておく必要があります。
また、defineComponent
に置き換えても動作上問題ない(型チェックのみ)であると判断し、一括で置き換えました。(140ファイル程度)
emits option の追加
Vue 3 では.native
が削除され、emits option
を追加することが実質必須になっています。
Vue 2系でemits option
を書いても動作上問題がないため、Migration Strategyのとおり emit を使っているコンポーネントに対してemits option
を追加しました。
また、emits option
を追加するにあたって対象のコンポーネントの数が多いので、自動化ツールを作成しました。
Vueファイルのtemplate
やscript
内で使用されているemit
からイベント名のみを抽出し、文字列の配列形式でemits option
に追加します。
(このツールは ts-morph を活用して作成しています。)
このツールのおかげで一括変換しリリースできました。
また、ESLint のルールである vue/require-explicit-emits を適用することで移行漏れを防止しています。
最後に
移行作業は大変ですがさまざまな知見を得られて非常に楽しいです。
(Nitro 等あたらしいライブラリのことを知れたり)
Nuxt 3 への移行に具体的にやったことなどは、また別の機会に共有できればと思っています。
まだまだ課題は山積みなので、一緒に解決してくださる方募集中です。