Nuxt2 から Nuxt3 への変更点を整理する

目次
1. 背景
2. 変更点

# 1. 背景
Nuxt2を業務で使用しています。年内にサポート切れするということでNuxt3に移行する必要がある為です。

https://nuxt.com/docs/migration/overview

# 2. 変更点
## 2.1 VuexはNuxt3本体に含まれていない
- [Vueの公式がpiniaを推奨してます](https://ja.vuejs.org/guide/scaling-up/state-management.html#pinia)
    - Nuxtモジュールとして提供されています。
    - 以下のライブラリが必要です。
        - [pinia](https://pinia.vuejs.org/)
        - [@pinia/nuxt](https://pinia.vuejs.org/ssr/nuxt.html)
        - [pinia-plugin-persist](https://www.npmjs.com/package/pinia-plugin-persist)、Storeの永続化（最終メンテが2年前なのが気になる）
- useState
    - Nuxt Bridge から useState が使えます。
pinia、vuex、useStateの比較記事はまた別で書こうと思います。

## 2.2 axiosではなくFetch APIか、unjs/ohmyfetchの$fetchを使う形になる
[Nuxt3でaxiosはなくなり、Fetch APIをつかうらしい](https://www.memory-lovers.blog/entry/2022/06/02/170000)
移行中で対応する時間がない場合は以下のようにすることで大丈夫だそうです。
```nuxt.config.ts
import { defineNuxtConfig } from "nuxt";

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
  vite: {
    server: {
      proxy: {
        "/api/": {
          target: "https://api.example.com",
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ""),
        },
      },
    },
  },
});
```

## 2.3 静的ファイルのディレクトリ名をstaticからpublicに変更

## 2.4 @nuxtjs/style-resources がnuxt3に対応していない
https://stackoverflow.com/questions/69953025/nuxt-3-resolver-resolvemodule-is-not-a-function

以下のように対応したら良いそうです。
nuxt2
```nuxt.config.ts
export default defineNuxtConfig({
  buildModules: ['@nuxtjs/style-resources'],
  styleResources: {
    scss: ['./assets/variables.scss'],
  },
});
```
↓
nuxt3
```nuxt.config.ts
export default defineNuxtConfig({
  vite: {
        css: {
            preprocessorOptions: {
                scss: {
                    additionalData: '@import "@/assets/scss/variables.scss";',
                },
            },
        },
    }
});
```

# 2.5 process.envから値が取得できない
nuxt2
```nuxt.config.js
console.log('ENV_NAME', process.env.ENV)
```
↓
nuxt3
```nuxt.config.js
console.log('ENV_NAME', useRuntimeConfig().public.ENV)
```

# 2.6 head プロパティ
headの上にappプロパティが必要です。(オブジェクトは重複排除のためのhidキーを持たなくなリます)。

nuxt2
```nuxt.config.js
export default {
  head: {
    titleTemplate: '%s - Nuxt',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: 'Meta description' }
    ]
  }
}
```
↓
nuxt3
```nuxt.config.js
export default defineNuxtConfig({
  app: {
    head: {
      titleTemplate: '%s - Nuxt',
      meta: [
        { charset: 'utf-8' },
        { name: 'viewport', content: 'width=device-width, initial-scale=1' },
        { name: 'description', content: 'Meta description' }
      ]
    }
  }
})
```
# 2.7 nuxt.configでCommonJS構文を使えない
>module.exportsやrequire、require.resolveのようなCommonJS構文を使わないようにしてください。これはすぐに非推奨となり、サポートされなくなります

[Update nuxt.config](https://nuxt.com/docs/bridge/overview#:~:text=Please%20make%20sure,possible%20and%20recommended.)

# 2.8 components ディレクトリの自動インポート
components ディレクトリ内に書いた vue コンポーネントは、 import 文無しで使うことができます。

例えば、components/header/HeaderSearchButton.vue という名前のコンポーネントは、 インポート無しで ```<HeaderSearchButton>```として使えます。

また、 components/header/SearchButton.vue という名前のコンポーネントも同じく、 ```<HeaderSearchButton>``` として使えます。

ディレクトリ名 + ファイル名 でコンポーネント名になりますが、ファイル名の先頭とディレクトリ名が重複する場合、重複する部分は打ち消されます。

# 2.9 Nuxt3だとページ遷移時にdocument.getElementByIdで要素取得できない
https://github.com/nuxt/framework/issues/3587
以下のようにすると良いそうです。
```sample.vue
<template>
  <div>
    <h1 ref="myElement">Hello Nuxt3!</h1>
    <!-- その他のコンテンツ -->
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'

export default {
  data() {
    return {
      myElement: ref(null)
    }
  },
  mounted() {
    if (this.myElement) {
      this.myElement.style.color = 'red'
    }
  }
}
</script>
```

# 2.10 プラグインの書き方の変更
nuxtApp.provide を実行することでで、システムグローバルに $xxxxx という名前で使える関数を定義できます。
テンプレート内でも、script setup 内でも使えます。

```plugins/filter.ts
export default defineNuxtPlugin((nuxtApp) => {

  /**
   * 金額をカンマ区切りに表示します。
   * $addComma(1000) => 1,000
   */
  nuxtApp.provide('addComma', function (val: number) {
    if (val === 0) {
      return '0'
    }

    if (!val) {
      return ''
    }

    if (typeof val === 'string') {
      val = Number(val)
    }

    return val.toLocaleString()
  })
})
```

# 参考
- [Nuxt3 でプロジェクトを作成するときのメモ (ついでにVite, HMR, Bootstrap5, icons)](https://qiita.com/mml/items/12749c04cd44fecd1f08)
- [Nuxt2(bridge)からNuxt3へのフレームワーク移行とその工数](https://labs.1-10.com/2023/01/20/2290)
- [当社プロダクトにおける、nuxt2→nuxt3 への移行時の記録](https://tech.torico-corp.com/blog/migrate-nuxt2-to-nuxt3-on-our-product/)