feat: Nuxt 2 から Nuxt 4 へ移行する - #560
Conversation
7cbeaef to
5ed43b1
Compare
pug と Options API は維持し、動かなくなったものだけを置き換えた。 srcDir: "." で既存のディレクトリ構造を保っているのでファイル移動は無い。 jest→vitest、nuxt-i18n→@nuxtjs/i18n、require→import、Node 20→24。
static/→public/、layouts/error.vue→error.vue、app.vue追加、pages/map/_map.vue→[map].vue。 Vue 3 で挙動が変わる箇所を修正: 同一要素のv-for+v-if、配列watcherにdeepが必要、 $i18n.locale/localesがref、head()はuseHeadへ。SSR不可のimportはクライアント動的import。 lib/mapConfigs.ts と KML の回帰テストを追加。
vue-i18n 9 でコンポーネント名と prop 名が変わり、旧名のままだと 未知のコンポーネント扱いで本文が丸ごと消えていた。例外は出ない。
defineNuxtComponent の head() は setup 時点で評価されるため this.locale が undefined になり、日本語でも常に "KamiMap" になっていた。
min-width のメディアクエリはソース順に出るため降順だと xs が lg より後勝ちし、 col-12_xs-6_lg-4 が PC で2列になっていた。Nuxt 2 のビルドはメディアクエリを 昇順にまとめ直していたため症状が出ていなかった。
翻訳のあるロケールと固定文へのフォールバックを両方テストする。 useHead が locale を見られているかは index.head.spec で見る。
nuxt-i18n v6 の既定は全ルートで振り分けるため、日本語端末で /en/ を開くと 日本語になっていた。i18n を導入した #299 の受け入れ条件に反する。 redirectOn: 'root' にすると / の自動振り分けは残り、明示の接頭辞は尊重される。
8fe5bf4 to
9877a81
Compare
NEKOYASAN
left a comment
There was a problem hiding this comment.
遅くなりすみません!全体的には素晴らしいです。
コメントを入れているものとは別に、以下の2点ご確認いただければと思います!
yarn run generate時の出力先が.output/publicに変わっているようなので、gh-pagesのpublish_dirを変更いただきたいです!mapprint/.github/workflows/gh-pages.yml
Line 40 in 9877a81
- VRTの差分としては出ていないようなのですが、背景画像が正しく解決されていなくて表示されていないようで...
assets/sass/_layout.scssの background-image を相対パスに変更する必要がありそうですmapprint/assets/sass/_layout.scss
Line 10 in 9877a81
| @@ -1 +1 @@ | |||
| 20.10.0 | |||
There was a problem hiding this comment.
この変更に合わせてworkflowのバージョン指定も変更いただくか、node-version-fileを使う形にしていただけるとよいかもです!
mapprint/.github/workflows/gh-pages.yml
Line 20 in 9877a81
Vue 3 は v-for の template ref の順序を保証しないため、表示範囲が変わって DOM が再利用されると別の地点の吹き出しが付いていた。 併せて、残ったマーカーの番号が古いままだったのを書き換えるようにした。
~assets は webpack のエイリアスで、Vite では未解決のまま残り画像が出力されない。
|
ありがとうございます。4点すべて直しました。 マーカーと吹き出しのずれ(must) ご指摘のとおり私の撤去時の欠陥です。原因は Vue 3 が 背景画像 gh-pages 背景の件はこちらの検証不備です。PR 本文に「一致 100.00%、差分 0px」と書きましたが、比較の閾値(1チャンネル12以下を無視)が消えた背景を飲み込んでいました。 閾値0で測り直すと修正前は 10.35% のピクセルが違っていました。現在は平均差 0.000/255、閾値0でも差分 0% です。本文の数字も直します。 手元の結果です。 |
NEKOYASAN
left a comment
There was a problem hiding this comment.
修正ありがとうございます!バッチリでした!
概要 | About
Nuxt 2.17.3 から Nuxt 4.5.1 に移行します。
pug と Options API はそのまま残しています。
base は #559(vue-mapbox 撤去)です。
そちらがマージされると base が #558、さらに master に切り替わります。
#559 がマージされるまでは Draft にしておきます。
Nuxt 2 は 2024-06 に EOL、Vue 2 は 2023-12 に EOL です。
Dependabot Alert の 59% はこの移行で解消します。
構造的に残るのは maplibre-gl 1.x 経由の
protocol-buffers-schemaだけで、これは maplibre-gl の更新を別 PR にします。既存の配置を動かさない方針にしています。
Nuxt 4 の既定 srcDir は
app/ですが、srcDir: "."とdir: { app: "app" }を指定してpages/components/lib/を今の場所に残しました。ファイル移動を伴う差分にすると、このPRのレビューが実質不可能になるためです。
主な入れ替え
nuxt-i18nv6 →@nuxtjs/i18nv9。iso→language、localePath/switchLocalePathは composable になったためsetupで公開@nuxt/test-utils/config)、Vue Test Utils v1 → v2generate.routes()→nitro.prerender.routes。全13ロケール × 4地図で 145 ルートを事前生成require()による設定読み込み →import.meta.glob(Vite では動的 require が使えない)static/→public/Vue 3 で黙って挙動が変わる箇所を5つ踏みました。
同一要素での
v-for+v-ifの禁止、配列の watcher にdeep: trueが必要になること、$i18n.localeとlocalesが ref になること、defineNuxtComponentのhead()が setup の戻り値も computed も見られないこと、data()からthis.$routeが引けないことです。いずれも例外が出ないため、テストと実機確認で見つけています。
動作確認方法 | How to check
Node は 24 系です(Nuxt 4 の要件)。
Vercel preview(fork 側の PR: yuiseki#24)
https://mapprint-git-yuiseki-migrate-nuxt-4-yuiseki-s-team.vercel.app/
kamimap.com と全ページ突き合わせて確認しました。
印刷日時を正規化して比較すると、事前生成した 65 ページすべてで差分ゼロです。
地図ページは地図領域が 96.71% 一致で、差は下部のマーカー番号だけです。
番号は
inBoundsMarkers.indexOf(marker) + 1で決まり、その並びはmounted()が各ソースを await せず読み込み完了順にlayers.pushするため run ごとに変わります。kamimap.com を 3 回開くと順序が変わることを確認しました。
このPRとは無関係な既存の挙動なので、直していません。
印刷される POI 一覧の内容(節・件数・地点名)は 4 つの地図ページすべてで一致しています。
スクリーンショット | Screenshot
kamimap.com(Nuxt 2)と このPR(Nuxt 4)の比較です。
1280x1024 の chromium、Accept-Language は ja-JP です。
読み込みタイミングが毎回変わる第三者ウィジェット(FB / X / LINE / GA)は両方で遮断しています。
Before(kamimap.com / Nuxt 2)
After(このPR / Nuxt 4)
Diff
赤が差分、灰色が一致です。
赤が 1px もありません。
平均絶対差 0.000/255、1 チャンネルでも差のあるピクセルが 0% です。
ja / en / zh / ko / th の 5 ロケールすべてで同じです。
はじめは差分の判定に「1 チャンネル 12 以下は無視」という閾値を使っていました。
文字のアンチエイリアスを除くためでしたが、それが
~assetsの解決に失敗して消えていた背景画像を飲み込んでいました(閾値 0 では 10.35% のピクセルが違っていました)。NEKOYASAN さんのご指摘で気づき、パスを直したうえで閾値 0 で測り直した数字が上記です。
挙動が変わる点 | Behavior change
ここだけ意図して kamimap.com と挙動を変えています。
detectBrowserLanguageをルートだけに限定しました(redirectOn: 'root')。nuxt-i18n v6 の既定は全ルートで振り分けるため、現在は日本語端末で
/en/を開くと/に飛ばされて日本語になります。これは i18n を導入した #299 の受け入れ条件「
/enで英語が表示されること」に反しています。redirectOn: 'root'にすると/の自動振り分けは今と同じまま、明示された接頭辞は尊重されます。/+ ネパール語端末/ne//ne//+ 英語端末/en//en//en/+ 日本語端末/日本語/en/英語外国人向けに URL を渡す使い方を想定すると後者が望ましいと考えましたが、製品判断だと思います。
今のままにすべきであれば戻します。
振り分けが起きる場合だけ
Hydration completed but contains mismatchesが出ます。事前生成された
/の日本語 HTML をクライアントが差し替えるためです。現在も同じ差し替えが起きていますが、Nuxt 2 は警告を出しません。
移行で必要になった修正 | Fixes needed for the migration
Nuxt 2 では症状が出ず、移行して初めて出るものです。
3 点とも先にテストを書き、壊れた状態で落ちることを確認してから直しました。
トップページのタイトル(
pages/index.vue)defineNuxtComponentのhead()は setup 経由で評価されるため、その時点でthis.localeがundefinedになり常に default 分岐に落ち、日本語でもKamiMapになっていました。useHeadに移しました。「このサイトについて」の本文(
components/Modal.vue)vue-i18n 9 で
<i18n path>が<i18n-t keypath>に変わりました。旧名は未知のコンポーネント扱いで本文が丸ごと消え、例外も出ません。
カードの列数(
assets/sass/vendor/gridlex/_gridlex-vars.scss)$gl-mq-listが降順で、min-widthのメディアクエリはソース順に出力されます。そのため 1280px では後に来る
xs-6(50%)がlg-4(33.3%)に勝ち、PC で 3 列ではなく 2 列になっていました。Nuxt 2 のビルドはメディアクエリを昇順にまとめ直していたため症状が出ていませんでした。
cssMinify: falseでも再現するので最小化のせいではなく、ソースの並び順そのものが以前から逆でした。別PRにしたいもの | Out of scope
protocol-buffers-schemaの Alert が残る)mounted()が読み込み完了順にlayers.pushするため、印刷される一覧とマーカー番号の並びが毎回変わる件pages/index.vueのcase "kr"はロケールコードの誤り(ko)。挙動を変えないためこのPRでは直していません