Nuxt2からNuxt3に移行するための備忘録

ブログをリニューアルする際に当初Nuxt2で作成していたため、Nuxt3へ更新することにしたのですが、まだあまりドキュメントが整備されていなかったり、情報が見つけられず躓いたりした部分が結構あったため、備忘録として記載します。

※ この情報は2022/4現在の情報です。もしかしたら一部間違っているかもしれません。

※ 結論としては既存のProjectをNuxt3に移行するのは時期尚早という認識になりました。

基本的には公式ドキュメントを参照してください(わかりづらいですが……)

nuxt.config.js

ページトランジション

一応実装されていますが、名前付きトランジションは未実装のようなので、名前を変えている場合はそれ用にCSSを修正する必要がありそうです。

// nuxt.config.js

pageTransition: {
    name: 'slide-bottom-fade',
},

この様になっている場合は下記のようにCSSのSelectorを修正する必要があります。

.page-leave-active,
.slide-bottom-fade-leave-active {}

// 以下省略

.page-{トランジションステップ名}の形ですね。つまりはトランジション名がpageになっています。

サーバーミドルウェア

serverMiddleware: [],で記載していましたが、不要になりserverディレクトリで動的にルーティングするようになりました。

メタ情報

Nuxt2ではhead: { meta: [], link: []}のように記載していましたが、Nuxt3ではmeta: { meta: [], link: []}というようにheadがmetaに変わりました。……ややこしい

ちなみにcharsetの配置も元headのmetaの方に移動になったようです。viewportとかと同列に書いていると2重で出力されます。

というか初期でutf-8が設定されているので、記述すらも不要かもしれません。

プラグイン

pluginsの記述は不要となり、自動インポートされるようになりました。

{ src: '@plugins/swiper', mode: 'client' }のようにクライアントのみ動かしたい場合はswiper.client.jsのようにサフィックスをつければいいようです。

サーバーのみなら{name}.server.js

routerミドルウェア

router: { middleware: [] }でルーティング毎に実行するjsを指定できましたが、こちらも自動インポートになりました。

ちなみにrouterオプション自体は機能するようですが、いまいちうまく動かなかったです。

ビルド

build: {}で色々設定をしてたりしますが、バンドラーがwebpackからviteになった関係で記述が変わります。(要ドキュメント参照)

node_module

一部のmoduleが不要になりました。

また自分が確認した限りでの未対応モジュールとその対策です。

  • @fortawesome/vue-fontawesome
    • → 2系から3系に変更
  • @nuxtjs/google-gtag
    • → 未対応。vue-gtag-nextに変更
  • @nuxt/components
    • → 自動読み込みになったため不要
  • @nuxt/postcss8
    • → 多分不要
  • @nuxtjs/axios
    • → 不要。useFetchもしくは$fetchで代用可能です。
  • @nuxtjs/color-mode
    • → 2系から3系に更新。ただしclient・serverレンダリングでのミスマッチになる不具合あり
  • @nuxtjs/feed
    • → 未対応。自作するか?(ちょっとめんどくさくて調べてないです)
  • @nuxtjs/pwa
    • → 未対応。自前でmanifestとserviceworkerを用意
  • @nuxtjs/recaptcha
    • → 未対応。recaptcha-v3に変更
  • @nuxtjs/sitemap
    • → 未対応。sitemapである程度自前にする必要がありそう。ただしnitro:generateフックがなくなる不具合あり
  • @nuxtjs/style-resources
    • → 未対応。scssで定義した変数などを使いたい場合は各コンポーネントで読み込むことで回避
  • cookie-universal-nuxt
    • → 不要。useCookieで代用可能。
  • cors
    • → 不要。viteのproxy設定で代用可能らしい。自分の環境ではうまく行かなかったため自前APIを経由するようにした
  • lodash
    • → 不要。デフォルトで含まれています。
  • vue-awesome-swiper
    • → 不要。作成者が本家がvue対応しているからそっちを使うことを推奨している
  • vue-infinite-loading
    • → 未対応。v3-infinite-loadingに変更。ただコンポーネントでwarnが出る?
  • vue-lazyload
    • → 3用のvue3-lazyloadに変更

ファイル & ディレクトリ

静的ファイルディレクトリ

favicon.icoなどの静的ファイルを配置するディレクトリとしてNuxt2ではstaticディレクトリがありましたが、Nuxt3ではpublicディレクトリに変更になったようです。

自動インポート

Nuxt3では一部のディレクトリのファイルは自動的にインポートされます。

いちいちimportだのnuxt.config.jsに記載だのしなくていいので非常に楽です。

  • components
    • nuxt.config.jsでの設定はなくても読み込みますが階層ごとにアッパーキャメルケースが必要になるので、不要の場合は従来どおり設定をする必要があります。
  • composables
    • Nuxt3で使用することになるuseState用のディレクトリ(だと思っている)
    • トップレベルのファイルとサブディレクトリ内のindexファイルのみ読み込みます。それ以外は手動で読み込む必要があります。
  • hooks
    • 旧Mixinをこのディレクトリで書き換えていますが、これが正しいのかはわかりません。
    • 手動で読み込む必要があります。
  • layouts
    • 従来どおり。ただしerror.vuenuxt.config.jsと同階層に移動しました。
  • middleware
    • 仕様が変わっているようなので詳しくは公式ドキュメント参照
    • とりあえずnuxt.config.jsに記載しなくても読み込まれました(サブディレクトリ以下は未検証)
  • pages
    • 従来どおり
  • plugins
    • トップレベルのファイルとサブディレクトリ内のindexファイルのみ読み込みます。それ以外は手動で読み込む必要があります。
  • server
    • API用にpages同様に動的ルーティングされます。
    • pagesとかぶった場合は知りません。

動的ページ

/articles/1みたいに1の部分を動的にルーティングしたい時にNuxt2では/pages/articles/_id.vueもしくは/pages/articles/_id/index.vueとして設定することができます。

_名称route.param.名称として使用できる形ですね。

Nuxt3ではここも変更になったようで[名称]という形式になったようです。

この変更の利点として、一つの階層で複数のパラメータを設定できるという点ですね。

/sample/[param1]-[param2].vueみたいな場合/sample/A-Bにアクセスするとroute.params.param1Aroute.params.param2Bと取得することができます。

ちなみに[param1]and[param2]のような感じでやると思ったように取得できませんでしたroute.params.param1and route.params.param2みたいな感じでパラメータの区切りがおかしくなりました。

あと[...array]とすると階層も動的になるらしく、/1/2/3にアクセスすればroute.params.arrayの返り値が['1', '2', '3']となります。

ただしこちらはトレイリングスラッシュがある(/1/2/3/みたいな)と['1', '2', '3'、 '']になるので、注意が必要です。

使い所はまだよくわかりませんが、URL設計がより細かくできるようになりますね。

error.vue

Nuxt2ではlayouts/error.vueで設定していましたが、Projectルートになったようです。(app.vueと同階層)

ただNuxt2から更新したせいか、そのままでは動かずハマりました……

結論からするとyarn.lockないしpackage.lockを一度削除してから再度nodeモジュールを再インストールすると解決します。

多分依存モジュールあたりの関係でしょうね。

あとNuxt2ではレイアウトを使用している場合はそのままdefault.vueを読み込んでくれてましたが、どうやら自動では読み込まれないようです。

// error.vue

<template>
  <NuxtLayout name="default">
    <!-- エラーページ処理 -->
  </NuxtLayout>
</template>

のようにレイアウトの指定が必要なようです。

ただこれ、エラー後にページ遷移するとレイアウトが切り替わったと判定されるようで、再度レイアウトファイルが読み込まれます。悲しいなぁ……

解決策無いんですかね……pages/error.vueとかもだめでしたし(そもそもエラーページが機能しなくなる)

ちなみにこの関係でlayouts/default.vueapp.vueに切り替えができませんでした……(うちのサイトはレイアウトが一つしかないのでapp.vueでもよかった)

app.vueに対してページと同じようにerror.vueが機能してくれるのが多分一番スッキリするんですけどね。

処理

mixin

mixin自体はなくなったようです。(未実装ではなく無くなった)

mixinでmeta属性を切り替える処理をしていたのですが、自分はhooksディレクトリに作成しました。

Nuxt2ではmixins/meta.jsを各ページでimport Meta from '~/assets/mixins/meta'; mixins: [Meta],のように使っていました。

// mixins/meta.js

export default {
  head () {
    const head = {};
    head.title = this.meta.title;
    return head;
  }
}

Nuxt3ではhooks/useSetMeta.jsを各ページでimport useSetMeta from '~/hooks/useSetMeta'; useSetMeta({ title: '記事一覧'});のように変更しました。

// hooks/useSetMeta

export default function useSetMeta (metaData) {
  const head = {};
  head.title = metaData.title;
  useHead(head);
}

この方法で正しいのかはわかりませんが、とりあえずこれで動きました。

emits & on

Nuxt2(vue2)ではコンポーネント間のデータのやり取りにイベントバスの方法を使っていましたが、vue3ではどうやら導入されていないらしく別の記述に変更しました。

Nuxt2ではthis.$nuxt.$on('name', method);で登録し、this.$nuxt.$emit('name', arg);で呼び出しをしていました。

Nuxt3ではprovide&emitsで対応していきます。provide('name', method);で登録し、$name(arg);で呼び出し。

それぞれ{ provide } useNuxtApp(); { $name } useNuxtApp();が必要です。

store

vuexが廃止されuseStateで簡易的に状態管理ができます。Nuxt公式的にはpinia推奨らしいです。

自分はそこまで使ってないのでuseStateで使ってます。ただしvue Developer toolでstoreとして読み込んでくれませんのでそこは注意です。

// composables/useStatus.js

export const useStatus = () => {
  const currentPageId = useState('currentPageId', () => 'unknown');
  return { currentPageId };
}

style

vueでは:style="{color:red}"のようにstyle属性にオブジェクトでCSSを記述することが可能でしたが、自分が試した感じですとなぜかSSR時には機能してくれませんでした……(Galleryページで使用していた)

ページ遷移で再レンダリングされるときは機能するんですけどね……

しかたないので、vue3の新機能である<style>に対してjs変数を当て込める方法で対処しました(幸い使っているところはコンポーネント化していたのでscoped styleでclassに当て込んでも問題なかったので)

<script setup>
const style = { color: red };
</script>

<style lang="scss" scoped>
.text {
  --color: v-bind(style.color);
  color: --color;
}
</style>

こうすることでcolor: red;として機能します。これ結構便利になりますね。(この場合は厳密には出力されるのはcss変数の--color: red;ですが)

QA

どの見出しにも入らなそうなのをQAという形で。。。

  • Q. なんか消したはずのscssファイルが読み込まれてエラーでるんだけど
    • A. Cookieを削除してください(デベロッパーツールのネットワークのキャッシュを無効にしてもこの現象が出ました)
  • Q. localhost:300{ローカルIP}:3000で挙動が異なるんだけど
    • A. Cookieを削除してください
  • Q. どのmoduleがNuxt3に対応してるの?
    • A. 各自で調べてください。最悪vue3用が使えるかもしれませんので対応してなければそちらを使用してください。
  • Q. プラグインから別のプラグインでprovide(inject)した関数を使いたいんだけどserverサイドで死ぬ
    • A. provide前に呼び出されているのが原因で、プラグインが自動読み込みになっておそらく名前順なので、ファイル名を変更したらもしかしたら行けるかも。nuxt.config.jsに従来どおり読み込み設定をすれば名前を変えなくても行けるのかな?

最後に

とまぁ既存のNuxt2ProjectをNuxt3にアップグレードを試してみましたが、まだ移行するには早いですね……

イチから作るProjectならNuxt3でもいいかもしれませんが。

でもvue3のscript setupの記述とかviteとか結構いい感じに作れるので、現時点ではNuxt2+unplugin-vue2-script-setupの組み合わせに留めたほうがいいかもしれません。

このブログも当初Nuxt3化しようと思いましたが、ブランチは塩漬けにして、そこからunplugin-vue2-script-setup版を生やしてそっちでReleaseを進めようと思います(´・ω・`)