Webフロントエンドエンジニアの飯塚です。
ワントゥーテンが展開しているQURIOS FIELDというデジタルツイン系サービスのwebフロントエンド開発を担当してます。
同サービスで年末にNuxt bridgeからNuxt3にフレームワークを移行したので、それについて書きます。
目的
Nuxt公式の移行ガイドがまだ作成中。
移行のためにやること調べることが色々あって結局どのくらい時間がかかるのかが見通しづらいので、私の場合の移行の進め方とざっくり実工数を共有します。
あくまで独自の移行の進め方なので、公式ガイドが公開されたらそちらを参考にすることを推奨します。
結論
移行の工数はざっくり7人日〜12人日
※1人日8h
記事中の赤文字で記した部分は計測した作業時間と修正したファイル数です。
その他の作業や作業のための調査時間は計測していなかったのですが、多めに見積もって所感は5~10人日ぐらいかと。
方針
可能な限りNuxt3でも動くようにNuxt bridge環境を調整してから、別リポジトリに作ったNuxt3環境にファイルを移していく。(←移行しやすかったのでおすすめ)
npmだとnuxt2と3のようなバージョン違いを同じリポジトリにinstallできなかったので、別の新しいリポジトリをつくりました。
最低限Nuxt2と同じように動くところまでが目的です。
composableディレクトリに処理を移す、componentを自動importさせるなどNuxt3のお作法に沿うところまではやっていません。
プロジェクトの規模と状態
※SSGサイト
※1年ほど一人で開発を進めてきて小~中規模くらい?の印象。
※ jsからTypescriptに移行中なので、js,tsが混在してます。
※ monorepo構造にしていて、共通コード以外のプロジェクトごとのコンポーネント、コードには重複しているファイルもあったりするので、管理しているファイル数は計測結果より少ない印象ではあります。
- Nuxt bridge移行済
Nuxt2 -> Nuxt bridgeへの移行は1人日程度だった記憶。工数はプロジェクトの状態によりけりかも。
- CompositionAPI対応済
作業詳細
以下、作業メモ的なものですが、詳細です。
旧リポジトリ(Nuxt2 bridge)の調整
○ Pinia導入 (所要時間 7.5h) – 6 files(vuex store file)
Nuxt3ではvuexでなくPiniaが推奨されている。
調べながら、Typescriptの型調整しながら約1人日程度。
○ .vueファイルのimport文変更 (所要時間 1.5h) – 41 files(.vue)
component dynamic? import文の書き方がvue3だとdefineAsyncComponentを使ったものに変わる 。移行前後で共通の書き方で動くように通常のimport文に直す。
[vue2 dynamic import]
components: {
ComponentA: () => import('./ComponentA')
}
↓
[vue3 dynamic import]
import {defineAsyncComponent} from 'vue'
components: {
ComponentA: defineAsyncComponent(() => import('./ComponentA'))
}
[通常のimport文] ←これに書き直す
import ComponentA from './ComponentA'
components: {
ComponentA,
}
※移行前後の書き方を統一するため、コンポーネントの自動importは不使用
○ @nuxt/axiosを削除して、axiosをinstall (所要時間 10min)
@nuxt/axiosは使っていなかったがaxiosは使っていました。 公式はfetchを推奨しているが、時間的な都合でとりあえずaxiosをinstallしました。
○ process.env.*からruntimeConfigへの移行するための書き換え (所要時間 45min) – 30files
nuxt3でprocess.envから値が取得できなかったので、Nuxt2では一度変数に入れて、それを使うようにしました。Nuxt3では当該変数の参照元をprocess.envからuseRuntimeConfig().publicに書き換える対応のみで済むようにしました。runtimeConfigとappConfigを分けるのはNuxt3移行後にやる想定
[Nuxt2] process.envへの直アクセスを廃止して一度変数に入れる
const project = process.env.project
Constants.runtimeConfig = {
project: process.env.project,
...
...
}
const project= Constants.runtimeConfig.project
↓
[Nuxt3]
Constans.runtimeConfig = useRuntimeConfig().public ←移行時はこの代入だけ書き変え
const project= Constants.runtimeConfig.project
○ $nuxtという書き方(global object)で参照している箇所をuseNuxtAppの使用に書き換え (所要時間 1h)
global objectの$nuxtを参照しているところがいくつかあったので、useNuxtApp()を使うようにしました。
[Nuxt2]
$nuxt.$t, $nuxt.$routerなど
↓
import { useNuxtApp } from '@nuxt/bridge/dist/runtime'
const { $t, $router } from 'useNuxtApp()
[Nuxt3]
const { $t, $router } from 'useNuxtApp()
○ nodeのassert文削除
nuxt3でnodeのassertを使用しているところで参照エラーが出たのですべて削除しました。
ここはtypescriptのassert関数を作成して置換するべきだったかも。
○ vueのscss部分についてdeepを変な書き方してたので、vue3の:deep(*)の書き方に変更 (所要時間 20min) – 6files
○ threejsのimport文追加 (所要時間 1h)
Nuxt2ではwebpackのproviderPluginでTHREEをimport文なしで読めるようにしていたのですが、Nuxt3(vite)でのやり方がすぐにはわからなかったので、以下のような普通のimport文を追加しました。
import { ***, ***, ... } from ‘three’
移行後、新リポジトリ(Nuxt3)の調整
nuxt.config.tsの調整
- typescript.shim をfalseにする。
代わりに、公式でTypeScript Vue Plugin (Volar)のTake Over Modeを推奨している。
export default defineNuxtConfig({
typescript: {
shim: false
}
})
- head を app.headに変更
- 静的ファイルのディレクトリ名をstaticからpublicに変更
aliasも@staticから@publicに変更 - @nuxtjs/style-resources がnuxt3に対応していない
参考)https://stackoverflow.com/questions/69953025/nuxt-3-resolver-resolvemodule-is-not-a-function - router.base を app.baseURLに変更
参考)router.baseを設定したNuxt2をNuxt3へマイグレーションする際の方法(app.baseURL)
Vue書き換え
- useNuxt2Meta → useHead 全置換
- $router.options.base → $config.app.baseURL 全置換
- buttonコンポーネントについて、click eventをemitする必要がなくなったので削除
<button @click.prevent="$emit('click')”>
↑このemitを書く必要なくなった
- NuxtChild→NuxtPageに変更
変更自体は簡単だが、変更した結果、NuxtPageのrefで参照したコンポーネントでdefineExposeしたプロパティにアクセスできなくてはまった。
(結局、exposeせずに動くように調整した)
[Nuxt3]
<NuxtPage ref="xxx" />
で設定したrefコンポーネント内でdefineExposeしたプロパティ・メソッドにアクセスできなかった。
[Nuxt2]
<NuxtChild ref="xxx" />
のrefコンポーネントのプロパティ・メソッドにはアクセスできていた。
- page transitionの書き方変更
参考リンク - vue3ではcomposition apiでcomponentのextendsができなくなっている?
(nuxt-bridgeのcomposition apiではextendsできていた。)
composablesで代替
Plugin書き換え
pluginの仕様が変わっている
https://nuxt.com/docs/migration/plugins-and-middleware/#plugins
- @nuxtjs/i18nの移行
– @nuxtjs/i18n@next install
– v7 to v8 migrating guide
– $nuxt.$t(’key’) で指定しているテキストを useI18n().t(’key’)に変更 - vue2-perfect-scrollbarの移行
→ vue3-perfect-scrollbar - vue-awesome-swiper
→ https://swiperjs.com/vue - floating-vue
→ upgrade https://floating-vue.starpad.dev/migration/migration-from-v3.html
→ https://floating-vue.starpad.dev/guide/installation.html#nuxt-3 - gtag plugin(自作)
→ vue-gtag-next
Middleware書き換え
- serverMiddlewareを使用したlocal環境のAPI mockをserverディレクトリに移行
- middlewareのformatが変わっているので調整
その他 Trouble
- nuxi generateでエラーが出たのでsoucemapの出力をなくした。
nuxt generate FATAL ERROR: Reached heap limit Allocation failed – JavaScript heap out of memory
export default defineNuxtConfig({
sourcemap: false
})
- https://github.com/nuxt/framework/issues/3587
Nuxt3だとページ遷移時にdocument.getElementByIdで要素取得できない(直ページアクセスなら取得可能)ので、refから要素(canvas)を取得しjs処理に渡していくようにした。 - https://github.com/nuxt/framework/issues/5183
local開発環境のserver側で[unhandledRejection] Cannot set headers after they are sent to the client
がでるclient側動作で問題なさそうなので一旦放置
感想
- 総じて移行してよかった。
– 思ったより簡単に移行できた。1ヶ月以上かかるかと思っていた。
– 移行の予定がある場合には早めの移行を勧める。
公式の移行ガイドが公開されるのを待つ必要はないような気がする。
– 移行後1月くらい経過したが大きな問題はない。 - 記事中に太字で記した、以下の方針が良かったのでおすすめ。
> 可能な限りNuxt3でも動くようにNuxt bridge環境を調整してから、別リポジトリに作ったNuxt3環境にファイルを移していく。 - 開発環境のストレスが減った。
– vite早い
– bridgeだとnuxiコマンドがうまく動かないケースがあったがその問題がなくなった
– 一部のライブラリでimport時にbabel変換する必要があったが、その必要がなくなった