🌲

Nuxt 3がパブリックベータ版になったので新機能の紹介や所感など

2021/10/21に公開

はじめに

2021/10/12 Nuxt.js 3(以下 Nuxt 3)がパブリックベータ版に移行しました 🎉!
これによって誰でも触れるようになったというわけで、今回早速試してみました的な記事です。
(そしてなぜか Zenn ではあまり話題になってない…)

Nuxt 3 の主な新機能

主な新機能は以下の通りです。

Nitro Engine による高速化/軽量化および Nuxt 内部での API 作成への対応

トップページの記載によれば、75 倍の軽量化が実現したそうです。
高速化に関しては数値まで公表されていないですが、実際にアプリを作成したところバージョン2とは別物レベルで早くなっていました!

さらに Nitro Engine の恩恵で「Nuxt 内部での API 作成」にも対応しました 🎉!
フレームワーク内部での API 作成に関しては、Next とか Svelte Kit にはありましたが、ついに Nuxt にも。
この API 機能、新しく登場した useAsyncData メソッドとの組み合わせで真価を発揮するのですが、詳しくは後述します。

Vue 3(Composition API 含む)のネイティブサポート

バージョン2の時点で一応対応されていましたが、Nuxt 3 では Vue 2 のサポートを切り捨てた上で対応しています。
それじゃあ Nuxt 2 からなんとか楽して移行したいけどどうするの…と思った人もいるかと。
これに関しては後述の Nuxt Bridge で対応できる仕組みになっています。

TypeScript のネイティブサポート

個人的にはこれが一番嬉しかったです!
他のライブラリだとオプションで TypeScript 設定をするパターンが殆どですが、Nuxt 3 に関してはそのままデフォルトアプリを作成するだけでそのまま使えるため、本当に驚きました。

ちなみに公式ドキュメントも全般的に TypeScript ベースでの内容になっておりかなり有り難いです。

Nuxt 2 からの移行用として Nuxt Bridge が新登場

Nuxt Bridge は「Nuxt 2 からの移行どうすればいい問題」を一気に解決してくれます。
Nuxt Bridge への移行方法はこちらに記載されています。
Vue 2 のまま Nuxt 3 の機能を体感できます。

なお(2021/10/21 時点)で Nuxt 2/Nuxt Bridge/Nuxt 3 の比較表はこんな形になっています。

ちなみに Nuxt Bridge を使わずに Nuxt 3 への移行する方法もここで記載されています。
ただし記載されているように修正箇所がかなり多いため、スムーズに移行したい場合は Nuxt Bridge への移行が推奨されています。

その他

その他以下の新機能/今後対応予定の機能もあります。

  • Webpack 5 に対応
  • Nuxt 2 までの Create Nuxt App に代わって Nuxt CLI が登場
  • Suspense 機能の追加
  • Nuxt Devtools(2021/10/21 時点ではまだ未対応)の登場

Nuxt 3 のインストール〜アプリ立ち上げ

必要環境

公式ドキュメントはこちら

以下の環境が公式で推奨されています。Vue 3 とほぼ同じです。

  • (必須) Node 14 or 16 がインストール済み
  • (推奨) vscode の使用
  • (推奨) Volarの使用
    • Vetur がオンになっている場合は Vetur 側を disable にしておいてください

Nuxt 3 アプリの新規作成〜ローカル立ち上げ

公式ドキュメントはこちら

前述した通り Nuxt 3 では Create Nuxt App に代わって Nuxt CLI を使用する形に変更されています。
そのため作成時のコマンドも、npx create-nuxt-app ... から npx nuxi ... になっています。

生成されるまでのコマンドは以下のとおりです。

npx nuxi init nuxt3-app

# vscodeを使用している場合はこのコマンドでフォルダをvscodeを開けます
code -r nuxt3-app

yarn install # or npm install

yarn dev -o # or npm run dev -- -o

最終的に http://localhost:3000 で Nuxt 3 アプリが立ち上がります。

Nuxt 3 のディレクトリ階層

公式ドキュメントでは以下のディレクトリ階層が紹介されています。

|
|── .nuxt
|── .output
|── assets
|── components
|── layouts
|── pages
|── plugins
|── public
|── server
|── .gitignore
|── app.vue
├── nuxt.config.ts
├── package.json
├── tsconfig.json

現時点(2021/10/21 時点)のドキュメントの記載によれば、Nuxt 2 から変わった点に関しては以下 2 点が明記されています。

  • static フォルダが public フォルダに変更
  • 内部 API 実装用として server ディレクトリが新しく追加
  • composables フォルダが新しく追加(※現在は未対応、ここの issue を見る限りもうすぐ対応されそう?)
    • Vue 3 で新登場した Composition API 用のコンポジション関数(Composition Function)を格納するフォルダ

※その他 Nuxt 2 から modules/middleware/store ディレクトリも消えているようにも見受けられるのですが、これに関してはドキュメントに記載がないだけか本当に使用不可になったのかまでは筆者は確認できていません。何か情報をお持ちの方がいたら遠慮なくコメントください。

Nuxt 3 で新たに追加されたメソッドの紹介

今回は公式でも目玉として紹介されている「useFetch/useAsyncData」「useState」について紹介します。

useAsyncData/useFetch でデータの Fetch を行う

公式ドキュメントはこちら

useAsyncData

まず useAsyncData です。
こちらは基本的に前述した「Nuxt 内部で作成した API」を非同期で呼ぶためのメソッドになります。

「Nuxt 内部で作成した API」を作るためにはまず <プロジェクトのルート>/server/api ディレクトリ配下に js/ts ファイルを設置する必要があります。
今回は次の関数を作ってみました。

server/api/hello.ts
let counter = 0;

export default () => {
  return {
    hello: "Hello World",
    counter: `Counter: ${counter + 1}`,
  };
};

作った API 関数を useAsyncData を使って、以下の形で vue のテンプレートに呼ぶことができます。

app.vue
<script setup>
  const { data } = await useAsyncData('hello', () => $fetch('/api/hello'))
</script>

<template>
  {{ data.counter }}
  <p>{{ data.hello }}</p>
</template>

するとブラウザには作成した API の返り値が表示されます。

この useAsyncData メソッドがすごいところは上記例の data が、API の定義箇所を読み取って自動的にPick 型で型定義してくれる点です。そのため vue テンプレート内でのコード補完もスムーズにやってくれます。基本的にこの手の API を叩く系のメソッドは request と response の型定義をする必要がありますが、useAsyncData メソッドに関してはその必要もありません。

上記例の const { data } 〜 だと次の形で型定義が行われます。

let data: Pick<
  {
    hello: string;
    counter: string;
  },
  "hello" | "counter"
>;

useFetch

もう1つ非同期処理用のメソッドとして useFetch もあります。
こちらは基本的に外部 API を叩くためのメソッドになります(Nuxt 内部で定義した API も使えます)。

今回は外部 API として有名なJSONPlaceholderから todos/1 を取得してみます。

app.vue
<script setup>
  const { data: todo } = await useFetch<string, any>("/todos/1", {
    baseURL: "https://jsonplaceholder.typicode.com",
  });
</script>

<template>
  {{ data.counter }}
  <p>{{ todo.id }} {{ todo.title }}</p>
</template>

useAsyncData/useFetch のベストプラクティス

また公式ドキュメントではベストプラクティスも併せて紹介されています。
「fetch する際は必要なプロパティだけ読み込むことを強く推奨します」な内容です。

useAsyncData/useFetch にはこの「fetch する際は必要なプロパティだけ読み込む」ことを簡単に実現してくれる options プロパティがあります。

useFetch の例で登場した JSONPlaceholder の todos/1 を再び例に挙げてみます。
返り値はこんな形で返却されます※公式リンク

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

ここから idtitle だけ取得したい場合はどうすればいいでしょうか?

useAsyncData/useFetch の pick プロパティに"id"と"title"を string 配列で指定するだけです。

app.vue
<script setup>
  interface ResData {
    id: number;
    title: string;
  }

  // useFetchは<string, responseの型>で型定義できる
  const { data: todo } = await useFetch<string, ResData>("/todos/1", {
    pick: ["id", "title"],
    baseURL: "https://jsonplaceholder.typicode.com",
  });
</script>

<template>
  {{ data.counter }}
  <p>{{ todo.id }} {{ todo.title }}</p>
</template>

useState でグローバルな State 管理を行う

公式ドキュメントはこちら

Vue でグローバルな State 管理といえば Vuex が今まで使われてきていましたが、Nuxt 3 では useState を使うことで Vuex を使うことなく、簡単に State が管理できるようになりました。

グローバルな State の初期化をするために、まず plugins/ 配下に plugin を定義する方法が公式では紹介されています。今回は helloworld という State を定義してみます。

plugins/helloworld.server.ts
import { defineNuxtPlugin, useState } from "#app";

export default defineNuxtPlugin((_nuxt) => {
  const helloworld = useState<string>("helloworld", () => {
    return "hello world!!!!";
  });
});

そして再度 useState を使って定義した State 名で呼び出すことで Vue のテンプレートに出力することが出来ます。

app.vue
<script setup lang="ts">
const helloworld = useState<string>("helloworld");
</script>

<template>
  <p>{{ helloworld }}</p>
</template>

サンプルコード

今回文中で紹介したサンプルコードは以下の GitHub リポジトリで記載しています。
参考になれば幸いです。

https://github.com/miily8310s/nuxt3-sample-app

おわりに

まだパブリックベータ版になったばかりなこともあり、まだドキュメントも含め情報が少ない状態ではありますが、Nuxt Bridge を通じた既存の Nuxt 2 ユーザーへのサポートや、今回紹介した Fetch 周りのメソッドがかなり扱いやすいこと、デフォルトで TS 対応がなされているなど個人的には好印象を受けました。
今後正式リリースに向けてまだまだ機能が追加(変更)されると思われます。ぜひ皆さんで期待していきましょう!

GitHubで編集を提案

Discussion