くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Nuxt2+BuefyからNuxt3+Nuxt UIに移行してみた

この記事はNuxt Advent Calendar 2023の 1日目の記事です。


12月1日で2周年を迎えることができた、
GoogleスプレッドシートJSON API化サービス
「SSSAPI」

sssapi.app

Nuxtをつかって開発をしていますが、
その少し前の11月にNuxt3に移行完了しました🎉🎉

Nuxt2のEOLもあり、約一年試行錯誤していたので、
どう移行していったかをまとめてみました〜

移行を考えている方の役に立てば(*´ω`*)

移行前後での構成

移行前後で使っていたパッケージや構成はこんな感じ。

before after
multi repo monorepo
npm pnpm workspace
Nuxt2 Nuxt3
Buefy + Bulma Nuxt UI + TailwindCSS
Vuex Pinia
Portal-Vue Nuxt3(<Teleport>)
VeeValidate v3 Nuxt UI + yup
nuxt-clipboard2 VueUse
@nuxtjs/axios Nuxt($fetch/ofetch)

Nuxt3やVueUseに便利な機能が増えたので、
なるべくそれを利用するように変更してる。

Nuxt UIが割とよく、
ダイアログや通知(Notification)、FormのValidationもカバーしてくれる。

また、CSS系は TailwindCSSなので、
スタイルのカスタマイズもしやすい。

移行完了までの経緯/流れ

Nuxt3対応をしようと調査しはじめたときに、
Buefyがネックに。。V3に対応しないとのこと。。

なので、どうしようかといろいろ悩むこと半年。。

検討したのは、以下の3つ

  • 完全な独自UIライブラリ
  • Element-Plus
  • daisyUI

完全な独自UIライブラリは、サポートされなくなるリスクは減るけど、
かなりコストがかかるので、断念。。

Element-Plusは、試してみたけど、
CSSのカスタマイズが難しいので、断念。。

daisyUIもTailwindCSSベースなので、割りと良かったけど、
Vueや素のTailwindCSSと組み合わせたときの煩雑さや、
ダイアログや通知などは独自になるので、保留。。

途中までは、daisyUIで置き換えていて、

  • Nuxt2.16/Vue2.7へのアップグレード
  • Vue2.7でのsetupへの書き換え
  • UIコンポーネントのdaisyUI化
  • Nuxt3化

という流れを考えていた。

ただ、BuefyがSassを使っていて、
TailwindCSSのためにPostCSSも入れるとなると、
いろいろ煩雑になり、進みが遅めだった。

そこに、Nuxt UIを見つけ、
触ってみるとかなりよかったので、Nuxt UIを利用する方向に転換。

決定してからは早く、 0からNuxt3+Nuxt UIで、新規に作り直した形。

移行の際によかったと思った設計

移行自体は9月末からはじめて、2ヶ月ほどで終えられた。
Nuxt UIなどの習熟を踏まえてなので、割と良い気がしている。

結果としてUIの置き換えだけになったので、
それ以外の部分を分けておいてよかった。

  • 型定義
  • 書き込み/読み込みの処理
  • 定数やutility

などなど、UIに関わらないところは別のディレクトリに分けておいたので、
ロジック部分などにはほぼ手を入れていない感じ。

移行時に見直した設計

イケていないと思っていた部分を直しつつ、
ディレクトリ構成や状態管理の方針も変更してみた。

ディレクトリ構成

デフォルトから大きく変え、以下のような感じにしてみた。

/
  _assets/css/            ... cssなどアセット関係
    main.css
  _domain/
    composables/          ... 共通で使う処理関係
      useCopy.ts
    service/              ... 読み書きのロジック関係
      AuthService.ts
  _store/                 ... ストア関係(globalなステート)
    authStore.ts
  _types/                 ... 型定義関係
  _widgets/               ... 共通なコンポーネント
  pages/
    index/
      _internal/          ... ページ固有のコンポーネントなど
        FooButton.vue
        useFooProvider.ts ... ストア関係(localなステート)
      index.vue

方針としては、よく一緒に使うものを近くにおくという形。

共通的なものは、プレフィックス_を付けて上になるように。

そのページでしか使わないコンポーネントやロジックは、
_internal配下にまとまるように。

そのままだと、pages配下はすべてrouteに認識されるので、
_からはじまるディレクトリ配下は含めないように設定しておく。

export default defineNuxtConfig({
  hooks: {
    "pages:extend"(pages) {
      // remove routes: pages/_*/
      function removePagesMatching(pattern: RegExp, pages: NuxtPage[] = []) {
        const pagesToRemove = [];
        for (const page of pages) {
          if (page.file == null) continue;
          if (pattern.test(page.file)) pagesToRemove.push(page);
          else removePagesMatching(pattern, page.children);
        }
        for (const page of pagesToRemove) {
          pages.splice(pages.indexOf(page), 1);
        }
      }
      removePagesMatching(/\/_.*\//, pages);
    },
  },
});

近くにあり、見つけやすくなったので、割と良い気がしている。

状態管理関係

いままでは、そのページでしか使わない状態なども、
vuexを使って状態管理していたけど、あまり必要ない気がしたため、

  • Globalな状態(Pinia Store)
    • 全ページで共通。認証情報など
    • _storeに配置
    • Localな状態からのみアクセス
  • Localな状態(Provider)

provide/injectを使ったLocalな状態を使うことで、
ページ固有の状態/ロジックをまとめられるので、
よりUIコンポーネントと分離しやすく、開発しやすくなった気がする。

パスパラメタ/クエリパラメタの活用

いままでは選択した項目などは、内部でのみ変更していたけど、
パスパラメタ/クエリパラメタを活用する形に変更した。

それを起点にProviderの初期化をおこなうようにしている。

適切にURLのパラメタを活用することで、
意図しない反映漏れなども防げるのでだいぶ気を使う点が減った気がする。


こんな感じでNuxt3+Nuxt UI対応をしていました!
設計なども改善したので、開発もしやすくなるはず。。

Nuxt3+pnpmでデプロイ速度も表示速度も爆速になったので、
かなり快適に(*´ω`*)

Nuxt3もNuxt UIもかなり頻繁に開発が進んでいるので、
これからもたのしみ(*´ω`*)

こちらもよろしくお願いたします!!

GoogleスプレッドシートJSON API化サービス 「SSSAPI」

sssapi.app