nuxt3.svg

【Nuxt 3】Nuxt 2と比較しながらはじめるNuxt 3

Nuxt3

はじめに

2022年11月16日に Nuxt3.0 stable(安定版)がついにリリースされました。 執筆時期を考えると既に3ヶ月が経ってしまっているのですが…。

2023年12月31日でVue 2のサポートが終了することを考えると、今後NuxtについてもNuxt 3を使わざるを得ないでしょう。 このブログもNuxt 2で作ったものなので移行を考えないといけないのですよね。

というわけで、ここではNuxt 3についてNuxt 2と比較しながら、片足の指先くらいを突っ込む程度の雑な感じで触れていきたいと思います。 詳細は後日別の記事にしたいと思っています。

公式:https://nuxt.com/

プロジェクトの作成

Nuxt 3のプロジェクトを作成するにあたり、Node.jsの16.11以上のLTS(長期安定版)が必要になるためあらかじめインストールしてください。 インストール方法などは割愛します。 これまでNuxt 2などを使用していた場合はNVM(Node Version Manager)でNodeを使い分けることをお勧めします。 こちらも詳細は割愛で。

エディタはVisual Studio Codeが推奨されており、さらにVolarという拡張機能を追加することが推奨されています。 TypeScriptを使用する場合は、TypeScript用のVolarも追加してください。 これまでVueの拡張機能としてVeturを使用していた場合は、無効にする必要があります。

プロジェクトを作成するには以下のコマンドを実行します。

> npx nuxi init <project-name>

作成されたプロジェクトに移動し、パッケージのインストールを行います。

> cd <project-name>
> npm install

その後以下のコマンドで開発サーバーを起動し、表示されたURLにアクセスすれば画面が表示されるかと思います。

> npm run dev

さて、Nuxt 2を経験したことがある方はけっこうな違いがあることに気がついていると思います。 中でもサーバーの起動がかなり早いですよね。

Nuxt 3では、Viteというビルドツールが採用されています。 Nuxt 2で採用されていたwebpackと比べると、Vite(フランス語で「素早い」)というだけあってかなり早いです。 サーバーの起動だけでなく、ホットリロードも瞬時に行われます。 ちなみにNuxt 3でもwebpackはサポートされています。

あとプロジェクト作成段階では、フォルダ構成が必要最低限になっています。 Nuxt 2では/pagesなどのフォルダは初期段階で作成されていましたが、Nuxt 3からは自分で作成する必要があります。 まぁ、余計なmdファイルを作成されているよりはいい気がしますかね。

フォルダ構成

Nuxt 3では以下のようなフォルダ構成になります。

フォルダ・ファイルフォルダ名の変更説明
/assetsスタイルシート(CSS、SASS)や画像などを配備
/components×コンポーネントを構成する単一ファイルコンポーネントを配備
/composables×コンポーサブルなファイルを配備
各コンポーネントに自動インポート
/layoutsページ共通のレイアウトを構成する単一ファイルコンポーネントを配備
/middlewareミドルウェア機能に関するファイルを配備
/pagesページを構成する単一ファイルコンポーネントを配備
/plugins外部パッケージの設定に関するファイルを配備
/public画像などの静的ファイルを配備(Nuxt 2では/static
/utilsjs(ts)ファイルを配備
各コンポーネントに自動インポート
app.vue-Nuxtアプリケーションのベースとなるファイル
error.vue-エラー用ページ
nuxt.config.js(ts)-Nuxtアプリケーションの設定ファイル

一部省略していますが、/pages/layoutsなど基本的なところはNuxt 2と変わりはないと思います。 いくつかのフォルダは、nuxt.config.jsdirによって対応するフォルダ名の変更が可能です。

以下よりNuxt 2と異なる点について補足していきます。

app.vue

Nuxt 2と大きく異なるのは、app.vueが作成されている点でしょうか。 Nuxt 3では、app.vueがあれば最小構成で動作します。 これはプロジェクト作成直後にページが表示されていることからもわかると思います。

Nuxt 2と同様に/pagesを用いたルーティングや/layoutsを使用する場合は、以下のように<NuxtPage><NuxtLayout>を使用します。

app.vue
<template>
  <div>
    <NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
  </div>
</template>

ちなみにapp.vueは必須ではなく、プロジェクトから削除してもNuxt 3で用意されているデフォルトのものが使用されるため問題ないようです。

composables

Vue 3からComposition APIが道入されたことにより、コンポーサブル(composable)という言葉を聞くようになりました。 これはコンポーネントの一部を別のファイルに切り出すことで、部品として再利用しやすくすることを指します。 以下は簡単な例です。

useCounter.js
export const useCounter = () => {
  const count = ref(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count: readonly(count),
    increment,
    decrement,
  }
}
MyComponent.vue
<script setup>
import { useCounter } from './useCounter.js'
const counter = useCounter();
</script>

<template>
  {{ counter.count }}
  <button @click="counter.increment">plus</button>
  <button @click="counter.decrement">minus</button>
</template>

これらコンポーサブルなファイルは/composablesに配備します。 /composablesに配備されたファイルは自動でインポートされるため、importの記述は不要になります。

Nuxt 3では、例えばVue Routerの処理はuseRouter()によって参照します。 Nuxt 2では、this.$routerと参照していたのでこの点も異なりますね。

<script setup>
const router = useRouter();

const toAboutPage = () => {
  router.push({path: '/about'})
}
</script>

その他コンフィグなどの情報もコンポーサブルとして取得します。

され、ここで少しだけ話が逸れますが、Nuxt 2ではコンポーネント間の状態管理の手段としてVuexを使用していました。 しかし、Nuxt 3にはデフォルトの状態でVuexは含まれていません。 そこで代わりに用意されているのがuseStateです。

useStateは以下のようにコンポーサブルに使用することが多いかと思います。

/composables/useCounter.js
export const useCounter = () => {
  const count = useState('counte', () => 0);
  const increment = () => count.value++
  const decrement = () => count.value--

  return {
    count: readonly(count),
    increment,
    decrement,
  }
}
/pages/index.vue
<script setup>
const counter = useCounter();
</script>

<template>
  {{ counter.count }}
  <button @click="counter.increment">plus</button>
  <button @click="counter.decrement">minus</button>
</template>

第一引数はkeyとなりますが、コンポーサブルとして定義する場合は不要なので適当な値を設定します。 扱いとしてはref()と同じため、valueによって値を参照します。

先述したようにVuexと役割は同じです。 例えばIndexページで更新した内容はそのまま保持され、別ページで参照しても更新後の値となります。

utils

/utilsは、/composablesと同様に配備したjs(ts)ファイルが自動でインポートされるようになっています。 /composablesとの違いは、たぶんありません。 先ほど例にあげたuseCounter.js/utilsに移しても同じく動作をします。

結局のところ使い手が役割を分けるために用意されているのだと私は解釈しています。(違ってたらすみません。)

例えばバリデーターのように純粋な処理として共通化しておきたいものは/utilsに配備するべきだと考えています。 自動インポートされて困る場合は/assetsに配備しましょう。

デプロイ

最後にデプロイについて少しだけ触れておきます。 デプロイについてもいくつかNuxt 2とは異なる点があります。

SSR(Server Side Rendering)

Node.jsのサーバーにデプロイする場合は、以下コマンドを実行します。

> npm run build

.outputにデプロイ対象のファイルが生成されます。 その中の.output/server/index.mjsを実行することでサーバーが起動します。

> node .output/server/index.mjs

SSG(Static Side Generator)

SSGとして生成する場合は、以下のコマンドを実行します。

> npm run generate

.output/publicまたはdistがデプロイ対象となります。 また以下によりプレビューを表示することができます。

> npm run preview

SPA(Single Page Application)

SPAを生成する場合は、nuxt.config.jsssrプロパティをfalseに設定します。 この値はデフォルトではtrueになっています。 そのため上記2つはssr: trueの状態でビルドしたことになります。

nuxt.config.js
export default {
  ssr: false,
}

そのあとはSSGと同じくnpm run generateを実行し、対象のフォルダをデプロイします。

おわりに

まだこの記事のために簡単にしか触れていないのですが、Composition APIのおかげかNuxt 2に感じていた複雑さが 少し緩和されているように感じました。

Nuxt 2の内容と被るところが多いかもしれませんが、基本的なところから記事にしていけたらいいなと思っています。 ブログの移行も少しずつやっていきます。たぶん…。