本記事は電通国際情報サービス Advent Calendar 2022の8日目の記事です。
執筆者は Xイノベーション本部 AI トランスフォーメーションセンター所属の山田です。
この記事では先日(2022年11月中旬)に安定版がリリースされた Nuxt 3 について紹介します。
なお、執筆時点での私たちのチームでは、本番プロジェクトへの Nuxt 3 の導入には至っていません。 本記事は、あくまで技術調査をしながら簡単なアプリケーションを開発してみた内容をまとめたものになります。
本記事で紹介するソースコードは以下のリポジトリで公開しています。
Nuxt 3 について
はじめに、簡単に Nuxt 3 について紹介しておきます。
Nuxt は Vue.js のフレームワークです。
冒頭でも述べましたが、2022年11月中旬に Nuxt 3 の安定版がついにリリースされました。
Announcing Nuxt 3.0 stable、 https://nuxt.com/v3
Nuxt 3 のリリースで大きいのは Vue 3 の正式なサポートです。
Vue 3 は 2020年9月にリリースされていましたが、Nuxt 2 ではサポートされていませんでした。
Vue 3 から追加された Compositon API などを利用するために、Nuxt 2 のプロジェクトで Nuxt Composition APIなどのパッケージを利用している人も多かったのではないでしょうか。
今回リリースされた Nuxt 3 は Vue 3 も公式にサポートしていますし、サーバエンジンも Nitro に刷新され、パフォーマンスも向上しています。
開発環境
本記事での利用した開発環境の情報を記載します。
- 開発環境/IDE:GitHub Codespaces
- リージョン:Southeast Asia
- スペック:CPU 4コア、メモリ 8 GB
- Node.js のバージョン管理ツール:nvm
- Node.js バージョン:v18.12.1
- パッケージマネージャー・バージョン:Yarn / 1.22.19
- ブラウザ:Google Chrome
せっかくなので、GitHub Codespaces を用いてみました。 Node.js のバージョン管理ソフトには GitHub Codespaces 上でデフォルトでインストールされていた nvm を利用しています。 Node.js のバージョンは LTS の最新バージョンである v18 系を、パッケージマネージャーには Yarn を使っています。
Nuxt 3 のプロジェクト作成と TypeScript の設定
Nuxt 3のプロジェクト作成はnpx nuxi init
コマンドで行います。
npx nuxi init ${プロジェクト名}
プロジェクト作成段階でできるディレクトリ構成は以下のようになります。
プロジェクト名 ├── README.md ├── app.vue ├── nuxt.config.ts ├── package.json └── tsconfig.json
TypeScript の設定
Nuxt はデフォルトだと TypeScript の厳格な型チェックが有効になっていないので有効にしましょう。
まずは TypeScript と vue-tsc を devDependency に追加します。
yarn add -D typescript vue-tsc
次に nuxt.config.ts
を以下のように編集します。
// https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ typescript: { shim: false, strict: true, typeCheck: true }, });
shim: false
としているのは、VSCode で TypeScript Vue Plugin (Volar) を利用するためです。
strict: true
にすることで、型チェックが厳格になります。
typeCheck: true
としているのは、開発時から型チェックを有効にするためです。Nuxt ではビルドパフォーマンスを最適化するためにデフォルトでは型チェックが行われません。
このあたりの詳細については以下の公式ドキュメントを参照してください。
Nuxt - Installation、 https://nuxt.com/docs/getting-started/installation
Nuxt - type-checking、 https://nuxt.com/docs/guide/concepts/typescript
また Nuxt 3 からは CLI での型チェックが実行できます。
yarn nuxi typecheck
もしも開発中のホットリロード時にビルドパフォーマンスを求めるならば、typeCheck: false
として CLI での手動でのチェックという選択肢もあります。
以下のように npm スクリプトに typecheck コマンドを登録しておいてもいいかもしれません。
{ "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt typecheck" } }
ESLint の設定
次に ESLint の設定をしましょう。
Nuxt では VSCode で開発する際には ESLint プラグインを利用することが推奨されています。
注意点として Prettier は無効にすることが勧められています。
Nuxt - use-eslint、 https://nuxt.com/docs/community/contribution#use-eslint
ということで、まずは ESLint を devDependency に追加します。
yarn add -D @nuxtjs/eslint-config eslint
そして ESLint の設定ファイル.eslintrc
を作成します。
touch .eslintrc
.eslintrc
は以下のように記述します。
{ "extends": [ "@nuxtjs/eslint-config-typescript" ] }
すでに Prettier を導入している開発者に配慮して、Prettier によるフォーマットがかからないようにしましょう。
.prettierignore
を用意し、プロジェクト内のすべてのファイルをフォーマット対象外にしてしまいましょう。
以下が.prettierignore
の記述内容になります。
# プロジェクト内のすべてのファイルをPrettierの対象外にする
**
.vscode/settings.json
を設定し、保存時に ESLint によるフォーマットがかかるようにしましょう。
{ "editor.codeActionsOnSave": { "source.fixAll": false, "source.fixAll.eslint": true } }
最後に npm スクリプトに lint コマンドを登録しておきましょう。
{ "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt typecheck", "lint": "eslint --ext .ts,.js,.vue .", }, }
これにより以下のコマンドで ESLint によるコードの静的解析、フォーマットを実施できます。
# 静的解析 yarn lint # フォーマット yarn lint --fix
簡単なアプリケーションを開発してみる
環境が整ったので、ここからは簡単なアプリケーションを開発しながら Nuxt 3 について触れていきましょう。
今回は The Cat API を利用した、猫を愛でることができるアプリケーションを作ります。
The Cat API - Cats as a Service. https://thecatapi.com/
The Cat API では API キーがなくても、10枚まではランダムに猫の画像が取得できます。 完成形のイメージとしては、取得した猫画像をグリッド形式で表示するアプリケーションとします。
コンポーネントの設計
はじめにコンポーネントを設計しましょう。
Vue.js はコンポーネント指向なフレームワークですので、画面デザインからコンポーネントを設計しておくのが大事です。
画像出典:コンポーネントの基本 - コンポーネントの構成、 https://v3.ja.vuejs.org/guide/component-basics.html
今回は手始めに以下のようなコンポーネント構成を考えましょう。
- index.vue … ページコンポーネント。API呼び出しはここで行う。
- CatCardList.vue … 猫画像のリストを表示するコンポーネント
- CatCard.vue … 正方形の猫画像1枚を表示するコンポーネント
ツリー形式で可視化すると以下のようになります。
pages/index.vue
の作成
はじめにpages
コンポーネントのindex.vue
の追加しましょう。
Nuxt 3 では nuxi add
コマンドで簡単にpages
コンポーネントを追加できます。
npx nuxi add page index
作成時点では以下のような形になっています。
<script lang="ts" setup></script> <template> <div> Page: foo </div> </template> <style scoped></style>
Nuxt 2 までの単一ファイルコンポーネント内では、
- template
- script
- style
という順番で記述されるのが一般的でしたが、Nuxt 3では
- script
- template
- style
の順で記述するのが一般的なようです。
また script 部分は<script setup>
構文での記述がベースとなっています。
<script setup>
構文の詳細については Vue.js 3 の公式ドキュメントを参照してください。
Vue.js - SFC
<script setup>
、https://v3.ja.vuejs.org/api/sfc-script-setup.html
app.vue の削除
Nuxt ではpages
配下の Vue ファイルでアプリケーションのルーティングが生成されます。
そのため、プロジェクト作成時点でのエントリポイントであるapp.vue
はpages
コンポーネントを作成後は不要になるため削除します。
rm app.vue
API 呼び出しでのデータ取得
pages/index.vue
で The Cat API を呼び出して、データを取得する処理を記述します。
外部API呼び出しには Nuxt 組込みのuseFetch
を利用します。
呼び出し前に取得するデータの型を定義しておきましょう。
今回、利用する The Cat API のレスポンスは以下のようになっています。
[ { "id": "5ni", "url": "https://cdn2.thecatapi.com/images/5ni.jpg", "width": 500, "height": 375 } ]
この情報をもとに型情報を定義しましょう。
型定義はtypes
ディレクトリ配下に記述することにします。
# typesディレクトリの作成 mkdir types touch types/index.ts
interface
でレスポンスの型を定義します。
export interface CatResponse { id: string; url: string; width: number; height: number; }
そして定義した型情報をインポートしつつ、useFetch
でのAPI呼び出しのコードを記述します。
<script lang="ts" setup> import { CatResponse } from '~/types' const { pending, error, data } = useFetch<CatResponse[]>( 'https://api.thecatapi.com/v1/images/search?limit=10' ) </script> <template> <div> Page: foo </div> </template> <style scoped></style>
useFetch
を利用する際に、インポート文を書いていないことに違和感がある方もいるのではないでしょうか?
Nuxt 3 では、Auto Imports 機能があり、Vue.js標準のref
やcomputed
Nuxt 組込みのuseFetch
などの関数を明示的にインポートすることなく記述できます。
Nuxt - auto-imports、 https://nuxt.com/docs/guide/concepts/auto-imports
動作確認
ここまででアプリケーションを起動し、Chrome の Vue.js devtoolsを確認しましょう。
# 開発サーバでアプリケーションを起動
yarn dev
Chrome の Vue.js devtoolsを確認すると、きちんとデータ取得できていることがわかります。
コンポーネントの作成
続いてコンポーネントを作成していきましょう。
コンポーネントもnuxi add
コマンドで簡単に追加できます。
# CatCardListコンポーネントの作成 npx nuxi add component CatCardList # CatCardコンポーネントの作成 npx nuxi add component CatCard
CatCard コンポーネントの作成
まずはより小さい階層のCatCard
コンポーネントを作成します。
CatCard
コンポーネントでは、props
を介して1つのCatResponse
オブジェクトを受け取ります。
そして画像のURLを<img>
タグのsrc
に渡します。
以下がCatCard
コンポーネントのコードになります。
<script lang="ts" setup> import { CatResponse } from '~/types' interface Props { catData: CatResponse; } defineProps<Props>() </script> <template> <div> <img class="card-img" :src="catData.url" alt="cute cat" :data-testid="catData.id" > </div> </template> <style scoped> .card-img { border-radius: 8px; width: 320px; height: 320px; object-fit: cover; } </style>
CatCardList コンポーネントの作成
次にCatCardList
コンポーネントを作成します。
CatCardList
コンポーネントでは、props
を介してCatResponse
オブジェクトの配列受け取ります。
そしてv-for
構文で配列を展開し、CatCard
コンポーネントを複数描画します。
以下がCatCardList
コンポーネントのコードになります。
<script lang="ts" setup> import { CatResponse } from '~/types' interface Props { catList: CatResponse[]; } defineProps<Props>() </script> <template> <div class="card-list" data-testid="cat-list"> <cat-card v-for="(cat, i) in catList" :key="i" :cat-data="cat" /> </div> </template> <style scoped> .card-list { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; } </style>
pages/index.vue
から作成したコンポーネントを呼びだす
作成したCatCardList
コンポーネントをindex.vue
から呼び出します。
コンポーネントについても Nuxt 側での Auto Imports が有効になっているため、明示的にインポートする必要はありません。
<script lang="ts" setup> import { CatResponse } from '~/types' const { pending, error, data } = useFetch<CatResponse[]>( 'https://api.thecatapi.com/v1/images/search?limit=10' ) </script> <template> <div v-if="!pending && data" class="content"> <cat-card-list :cat-list="data" /> </div> </template> <style scoped> .content { margin: auto; } </style>
CatCardList は data
の値をプロパティにわたす必要があるためv-if
で条件付きレンダリングにします。
あとは、少しレイアウトを整えれば以下のようなアプリケーションが完成します。
テスト
最後にテストについても触れましょう。
Vue.js 3 系からはテストツールとして Vitest が推奨されています。
Vue.js - テスト #推奨事項、 https://ja.vuejs.org/guide/scaling-up/testing.html#recommendation
Nuxt 3 の公式ドキュメントではテスト用ツールに@nuxt/test-utils-edge
が紹介されていますが、こちらは開発中で不安定です。
なので今回はコンポーネントレベルのテストだけを実施する Vue Testing Library(@testing-library/vue)を用いる方法を紹介します。
なお Vue.js のテストツールとしては、 Vue Test Utils もありますが、コンポーネントレベルのテストでは@testing-library/vue
の利用が推奨されています。
Vue.js - テスト コンポーネントのテスト#推奨事項、 https://ja.vuejs.org/guide/scaling-up/testing.html#recommendation-1
準備
まずは必要なパッケージをインストールします。
テスト時にコンポーネントを描画するDOM環境にはhappy-domを使用します。
yarn add -D vitest @testing-library/vue happy-dom
Vitest の設定ファイルvitest.config.ts
を作成します。
/// <reference types="vitest" /> import { defineConfig } from 'vitest/config' import Vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [Vue()], resolve: { alias: { '~': `${__dirname}` } }, test: { root: '.', globals: true, environment: 'happy-dom' } })
npm スクリプトに test コマンドを追加します。
{ "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare", "typecheck": "nuxt typecheck", "lint": "eslint --ext .ts,.js,.vue .", "test": "vitest", }, }
コンポーネントレベルのテスト
CatCard
コンポーネントのテストを記述しましょう。
CatResponse
形式のオブジェクトをprops
に渡した際に、コンポーネントが描画できるかテストします。
テストコードはtests
ディレクトリ配下に*.spec.ts
の形で配置します。
@testing-library/vue
でのテストコードは以下のようなります。
コンポーネントが描画できているかの判断にはdata-testid
属性を使っています。
import { describe, expect, test } from 'vitest' import { render } from '@testing-library/vue' import CatCard from '~/components/CatCard.vue' import { CatResponse } from '~/types' describe('CatCard', () => { test('コンポーネントの描画ができること', () => { const catData: CatResponse = { id: 'test', url: 'https://example.com', width: 100, height: 100 } const { getAllByTestId, html } = render(CatCard, { props: { catData } }) const results = getAllByTestId(catData.id) expect(results.length).toBe(1) expect(html()).contain(`data-testid="${catData.id}"`) }) })
テストを実行してみましょう。
yarn test
きちんとテストが通れば、以下の出力が得られます。
RERUN tests/components/CatCard.spec.ts x20 ✓ tests/components/CatCard.spec.ts (1) Test Files 1 passed (1) Tests 1 passed (1) Start at 09:40:00 Duration 230ms PASS Waiting for file changes... press h to show help, press q to quit
Vitest の VSCode プラグインとデバッグ
テストコードを書くことによって、小さい範囲でコードを動かせるようになりました。
さらにデバッグもできると開発がより捗ります。
Vitest では公式で VSCode のプラグインをリリースしており、こちらを活用することでテストコードをもとにデバッガーを起動できます。
Vitest、 https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer
VItes - IDE Integrations 、 https://vitest.dev/guide/ide.html
テストコードにブレークポイントを仕込んで、テストタブから「テストのデバッグ」を選択するとデバッガーが起動できます。
これで開発環境はバッチリですね。
まとめ
本記事では、Nuxt 3 について紹介しました。
今回、記事を書きながら 、これまでの Nuxt の良さを引き継ぎながら正当に進化していると感じました。
nuxi
コマンドの充実や TypeScript、VSCodeとの親和性の向上、Nitroによるビルド速度の向上など開発がより楽しくなる可能性を感じました。
一方でテストの部分はまだ不安定だと言わざるを得ません。
Nuxt 側の Auto Imports 機能と Vitest の連携がうまくいかなかったり @nuxt/test-utils-edge モジュールが開発中であるため Nuxt 3 のアプリケーションをテストする方法が確立していません。
support for unit testing in a nuxt environment #2465、 https://github.com/nuxt/framework/issues/2465
Nuxt - Testing、https://nuxt.com/docs/getting-started/testing#testing
直近で Nuxt 3 の導入を検討しているならば、この部分には注意をする必要があります。
最後に、私たちAIトランスフォーメーションセンターでは、一緒に働いてくれる仲間を探しています。 AI 製品開発に興味がある方のご応募をお待ちしております。
執筆:@yamada.y、レビュー:寺山 輝 (@terayama.akira) (Shodoで執筆されました)