VuetifyもVue3に対応(一部除く)したようなので、Nuxt BridgeからNuxt3へのアップグレードしてみました。
変更が多くかなり大変なので、何回かに分けてナレッジをメモしていきます。

対象は、Nuxt.jsベースアプリケーション で公開しているdevelopとdevelop_spaceブランチです。
使っているパッケージや実装により、別途対応が必要になりますが、参考になれば幸いです。

Nuxt2をNuxt Bridgeに移行してみる

アップグレード方針

過去のcommitを残したいので、現在のリポジトリを使う。
まっさらなNuxt3アプリを作って、参考に動かしながら、変更や不要なファイルを削除して行く。
package.jsonやnuxt.config.ts(.js)は、最低限でスタートして、追加して行く。
先ずは、ページが表示されるようにして、課題を解消して行く。

やる事が多いと破綻するので、今回やらない事を決めます。
Options APIはVue3でも動くので、Composition APIへの書き換えはスコープから外す。
同様にJavaScriptからTypeScriptへの書き換えも、設定等以外はスコープ外。
Mixinも止めたいけど、動くのでスコープ外。
JestからVitestへの変更は後ほど検討。→ 対応できました。
Nuxt BridgeをNuxt3に移行。VitestとVue Test Utilsを導入

アップグレード可能か確認

Upgrade guide — Vuetify

Vuetifyは、現段階では下記には対応していない。

v-calendar
v-date-picker
v-data-table
v-skeleton-loader
v-stepper
v-time-picker
v-treeview

使用している場合は代替え可能か、リリースを待つかを決める。
Nuxt.jsベースアプリケーションのmaster/developは未使用なので続行します。
space_master/space_developは、v-data-tableを使っているので、Vuetify Labs. のを試す?
Vuetify Labsとv3-infinite-loadingでv-data-tableを暫定対応する

忘れん坊 はv-calendar(Vuetify Labs.も未対応)とv-stepperを使っているので暫く待ってみて、場合によっては代替え探す事にします。

Node.jsアップデート

$ nvm ls-remote | grep 'Latest LTS'
       v16.20.2   (Latest LTS: Gallium)
       v18.18.2   (Latest LTS: Hydrogen)
        v20.9.0   (Latest LTS: Iron)

% nvm install v20.9.0
% nvm alias default v20.9.0

% nvm ls
->      v20.9.0
         system
default -> v20.9.0

まっさらなNuxt3を作る(参考用)

% npx nuxi init nuxt-app
Ok to proceed? (y)
● yarn
● Yes

% cd nuxt-app
% yarn install

% yarn dev -o

差分を確認して、追加・変更(Nuxt3化)

ディレクトリ構成の説明が解りやすかったので参考にさせて頂きました。
【Nuxt 3】Nuxt 2と比較しながらはじめるNuxt 3

staticディレクトリをpublicに変更

リネーム: static -> public

server/tsconfig.jsonをコピペ

{
  "extends": "../.nuxt/tsconfig.server.json"
}

.gitignoreを変更

coverageとrobots.txtだけ元のを残しました。

# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist

# Node dependencies
node_modules

# Logs
logs
*.log

# Misc
.DS_Store
.fleet
.idea

# Local env files
.env
.env.*
!.env.example

# Coverage directory used by tools like istanbul
coverage*

/public/robots.txt

app.vueをコピペして、変更

before

<template>
  <div>
    <NuxtWelcome />
  </div>
</template>

after

<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

nuxt.config.tsをコピペして、変更

nuxt.config.jsは削除
CSRを使っているので、「ssr: false」を追加しています。

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  ssr: false
})

package.jsonを変更

ポートを5000番にしたいので、「–port 5000」を追加しています。

{
  "name": "nuxt-app",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev --port 5000",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "@nuxt/devtools": "latest",
    "nuxt": "^3.8.2",
    "vue": "^3.3.8",
    "vue-router": "^4.2.5"
  }
}

tsconfig.jsonを変更

{
  // https://nuxt.com/docs/guide/concepts/typescript
  "extends": "./.nuxt/tsconfig.json"
}

動かしてみる

予定通り?エラーになりました。

% rm -f yarn.lock
% yarn install
% yarn dev -o

 WARN  [warn] [nuxt] Plugin /Users/xxxx/workspace/nuxt-app-origin/plugins/application.js
 is not wrapped in defineNuxtPlugin.
 It is advised to wrap your plugins as in the future this may enable enhancements.

 WARN  [warn] [nuxt] Plugin /Users/xxxx/workspace/nuxt-app-origin/plugins/axios.js
 is not wrapped in defineNuxtPlugin.
 It is advised to wrap your plugins as in the future this may enable enhancements.

 WARN  [warn] [nuxt] Plugin /Users/xxxx/workspace/nuxt-app-origin/plugins/utils.js
 is in legacy Nuxt 2 format (context, inject) which is likely to be broken and will be ignored.

 ERROR  Failed to resolve import "vee-validate" from "components/users/update/Image.vue". Does the file exist?

 ERROR  Failed to resolve import "vee-validate" from "components/users/update/Data.vue". Does the file exist?
 ERROR  [worker reload] Cannot read properties of null (reading 'address')   

Error: [worker reload] Cannot read properties of null (reading ‘address’) · Issue #24423 · nuxt/nuxt · GitHub

This issue will be resolved in the next patch version of nitro.
 In the meantime, you can safely ignore it.
この問題は、nitro の次のパッチ バージョンで解決される予定です。それまでは、無視しても問題ありません。

plugin.apply is not a function

Mixinをpluginsディレクトリに置いていましたが、
自動インポートしようとしてエラーになるので、別のディレクトリに移動します。
TODO: mixinを止めた方が良いけど、先ずは移行が目的なので、後で対応。

ファイルを移動して、パスを変更

- import Application from '~/plugins/application.js'
+ import Application from '~/utils/application.js'

Cannot read properties of undefined (reading ‘onRequest’)

plugins/axios.jsで認証ヘッダを追加・保存していました。axiosは無くなったので、削除して、
TODO: 後で、Fetch APIに書き換えます。
Nuxt BridgeをNuxt3に移行。$axiosをFetch APIに書き換える。nuxt-lodashを導入

Failed to resolve import “@inotom/vue-go-top”

現段階でVue3に対応していないので削除して、
TODO: 後で対応検討します。
Nuxt BridgeをNuxt3に移行。上に戻るボタンに対応

[plugin:vite:import-analysis] Failed to resolve import “@inotom/vue-go-top” from “layouts/default.vue”. Does the file exist?

試したメモ(却下)

% yarn add -D @inotom/vue-go-top

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘_c’)

% yarn remove @inotom/vue-go-top

コードから削除

layouts/default.vue

-    <go-top :max-width="48" :size="48" :right="24" :bottom="24" bg-color="#1867c0" />

- import GoTop from '@inotom/vue-go-top'

  components: {
-    GoTop,

Cannot read properties of undefined (reading ‘breakpoint’)

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘breakpoint’)

暫定対応(置換)

「$vuetify.breakpoint.width」->「$vuetify?.breakpoint?.width || 1300」

TODO: 後で、Vuetifyを入れる時に対応してから書き換えます。
Vuetify: $vuetify.breakpoint.width -> $vuetify.display.width

Cannot read properties of undefined (reading ‘loggedIn’)

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘loggedIn’)

暫定対応(置換)

「$auth.loggedIn」->「$auth?.loggedIn」

TODO: 後で、authを入れる時に対応してから書き換えます。
Nuxt BridgeをNuxt3に移行。nuxt-authを導入

_ctx.$t is not a function(i18nを導入)

Uncaught (in promise) TypeError: _ctx.$t is not a function

@nuxtjs/i18n(現段階では7.3.1)だとエラーになるので、

 ERROR  [worker reload] [worker init] Named export 'stringify' not found.
 The requested module 'file:///Users/xxxx/workspace/nuxt-app-origin/node_modules/devalue/dist/devalue.umd.js'
 is a CommonJS module, which may not support all module.exports as named exports.

@nuxtjs/i18n@next(8.0.0-rc.5)を導入します。
Setup · @nuxtjs/i18n

% yarn add -D @nuxtjs/i18n@next

before(nuxt2): nuxt.config.js

  modules: [
    '@nuxtjs/i18n',
  ],
  i18n: {
    locales: [
      { code: 'ja', iso: 'ja', file: 'ja.js' }
    ],
    defaultLocale: 'ja',
    lazy: true,
    langDir: 'locales/'
  },

after(nuxt3): nuxt.config.tsを変更

export default defineNuxtConfig({
  devtools: { enabled: true },
  ssr: false
+  ssr: false,
+  modules: [
+    '@nuxtjs/i18n'
+  ],
+  i18n: {
+    vueI18n: './i18n.config.ts'
+  }
})

i18n.config.tsを追加

import { ja } from '~/locales/ja'

export default defineI18nConfig(() => ({
  legacy: false,
  locale: 'ja',
  messages: {
    ja
  }
}))

locales/ja.js -> .tsに変更

折角なので、tsに変更します。(jsonでも良いけど、自由度が高いので)

- module.exports = {
+ export const ja = {
  app_name: 'NuxtAppOrigin',
  <省略>
}

$configがundefinedになる(runtimeConfig設定)

before(nuxt2): nuxt.config.js

const environment = process.env.NODE_ENV || 'development'
const envConfig = require(`./config/${environment}.js`)
// eslint-disable-next-line nuxt/no-cjs-in-config
const commonConfig = require('./config/common.js')

export default defineNuxtConfig({
  publicRuntimeConfig: Object.assign(envConfig, commonConfig),

publicRuntimeConfigは、runtimeConfig.publicに変更されています。

after(nuxt3): nuxt.config.tsを変更

+ import { commonConfig } from './config/common'
+ 
+ const environment = process.env.NODE_ENV || 'development'
+ const config = require(`./config/${environment}.ts`)

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  ssr: false,
+   runtimeConfig: {
+    public: Object.assign(commonConfig, config.envConfig, { env: { production: environment === 'production', test: false } })
+   },

config/common.js -> .tsに変更

こちらも折角なので、tsに変更します。(jsonでも良いけど、自由度が高いので)

- module.exports = {
+ export const commonConfig = {
  // ログインAPI
  authSignInURL: '/users/auth/sign_in.json',
  <省略>
}

development/test/production.js -> .tsに変更

- module.exports = {
+ export const envConfig = {
  debug: true,
  <省略>
}

呼び出し方も変わっているので、置換

「$config.」->「$config.public.」

Vuetifyを導入

【公式】Get started with Vuetify 3 — Vuetify
GitHub – vuetifyjs/vuetify: 🐉 Vue Component Framework
-> MIT licensed


% yarn add -D vuetify sass vite-plugin-vuetify @mdi/font

TODO: @mdi/fontは@mdi/jsを使った方が小さくなるらしいけど、先ずは移行が目的なので、後で検討

plugins/vuetify.tsを追加

import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(createVuetify({
    components,
    directives
  }))
})

nuxt.config.tsを変更

+ import vuetify from 'vite-plugin-vuetify'
import { commonConfig } from './config/common'

  i18n: {
    vueI18n: './i18n.config.ts'
-  }
+  },
+  css: [
+    'vuetify/lib/styles/main.sass',
+    '@mdi/font/css/materialdesignicons.css'
+  ],
+  build: {
+    transpile: ['vuetify']
+  },
+  hooks: {
+    'vite:extendConfig': (config) => {
+      config.plugins!.push(vuetify())
+    }
+  },
+  vite: {
+    ssr: {
+      noExternal: ['vuetify']
+    },
+    define: {
+      'process.env.DEBUG': false
+    }
+  }
})

Vuetify: $vuetify.breakpoint.width -> $vuetify.display.width

window.innerWidthだと、template内で変更が反映できなかったので、
vuetifyのuseDisplayで取得するように変更しました。
$vuetify.display.widthで取得できました。但し、dataの段階ではundefinedになる。

node_modules/vuetify/lib/composables/display.mjs

    state.xs = xs;
    state.sm = sm;
    state.md = md;
    state.lg = lg;
    state.xl = xl;
    state.xxl = xxl;
    state.smAndUp = !xs;
    state.mdAndUp = !(xs || sm);
    state.lgAndUp = !(xs || sm || md);
    state.xlAndUp = !(xs || sm || md || lg);
    state.smAndDown = !(md || lg || xl || xxl);
    state.mdAndDown = !(lg || xl || xxl);
    state.lgAndDown = !(xl || xxl);
    state.xlAndDown = !xxl;
    state.name = name;
    state.height = height.value;
    state.width = width.value;
    state.mobile = mobile;
    state.mobileBreakpoint = mobileBreakpoint;
    state.platform = platform.value;
    state.thresholds = thresholds;

暫定対応した置換を変更

「$vuetify?.breakpoint?.width || 1300」->「$vuetify.display.width」

ただ、dataの段階では取得できないので、createdでセットするように変更しています。

  data () {
    return {
-      drawer: this.$vuetify.display.width >= 1264 // NOTE: md(Medium)以下の初期表示はメニューを閉じる
+      drawer: null

-  }
+  },
+
+  created () {
+    this.drawer = this.$vuetify.display.width >= 1264 // NOTE: md(Medium)以下の初期表示はメニューを閉じる
+  }
}

pagesが表示されない

layouts/default.vue

-        <nuxt />
+        <slot />

表示はされましたが、vee-validateがないのでエラーになりました。
次回は、ここから記載します。
デザイン崩れもあったりで、まだまだ対応が必要そうな予感。
Nuxt BridgeをNuxt3に移行。vee-validateを導入

今回のコミット内容

origin#507 差分を確認して、追加・変更(Nuxt3化)
origin#507 Mixinのディレクトリ変更
origin#507 エラー暫定対応
origin#507 i18nを導入、runtimeConfig設定
origin#507 Vuetifyを導入
origin#507 上に戻るボタン追加、widthの取得方法変更
origin#507 バージョンアップ、リファクタ

Nuxt BridgeをNuxt3に移行。先ずはVuetifyでページが表示される所まで” に対して1件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です