19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

NuxtAdvent Calendar 2022

Day 7

Nuxt3で全部入りのテンプレートプロジェクトを作る

Last updated at Posted at 2022-12-06

本記事はNuxt Advent Calendar 2022の7日目です。

Nuxt3がStableになりましたね。
私も少し先取りして3.0.0-rc.12のあたりから使ってみていましたが、そろそろいい感じに動いているので色々盛り込んたテンプレートプロジェクトを作って今後も使いまわしたいと思います。

今回作るプロジェクトの概要

以下を含むプロジェクトを作成し、今後作るアプリのベースとして使います。

  • フレームワーク
  • 開発ツール
  • SSRはオフ
  • 最低限の画面実装
    • よくある「ユーザー一覧」「ユーザー詳細」の画面遷移と、APIリクエスト処理

ソースコードは以下で公開しています。

プロジェクト生成

プロジェクトは以下のコマンドで生成します。

$ npx nuxi init my-nuxt-template

生成したら、ひとまずyarn installでyarn.lockを生成します。

$ yarn install

VSCode向けの設定

EditorConfig用の設定ファイルを作成します。

.editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_size = 2
indent_style = space
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

これは好みが分かれる所だと思いますが、私は新規開発者がソースコードをcloneしてきて開いた際にConfig Default拡張で .vscode/settings.json を生成したいので、以下の設定ファイルをコミットしておきます。
Config Default拡張をインストールした状態でこのプロジェクトを開くと、同じ内容で .vscode/settings.json が自動生成されます。

.vscode/settings.default.json
{
  "editor.formatOnSave": true
}

また、推奨拡張機能も列挙しておきます。

.vscode/extensions.json
{
  "recommendations": [
    "spadin.config-defaults",
    "editorconfig.editorconfig",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "vue.volar"
  ]
}

.gitignore の記載としては、よくある .vscode/* をignoreするような記載ではなく、settings.jsonのみをignoreしておきます。

.gitignore
/.vscode/settings.json

ESLint及びPrettierの導入

NuxtプロジェクトにESLintを追加する場合、 @typescript-eslint/parser 等を個別に追加する代わりに @nuxtjs/eslint-config-typescript で一括で設定できるようです。

$ yarn add -D @nuxtjs/eslint-config-typescript eslint

package.jsonのscriptsにlintを追加しておきます。

package.json
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "lint": "eslint .", // 追加
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },

また、PrettierもESLintと連携して動作するようにしたいので、以下のパッケージを追加します。

$ yarn add -D eslint-config-prettier eslint-plugin-prettier prettier typescript

設定ファイルは以下のようになります。セミコロン有無等は好みによって変えてください。

.eslintrc.cjs
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    "@nuxtjs/eslint-config-typescript",
    "prettier",
    "plugin:prettier/recommended",
  ],
  parserOptions: {
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["prettier"],
  rules: {},
}
.prettierrc.json
{
  "semi": false
}

Vuetify 3の追加

(Nuxt Advent Calendar 2022 2日目のNuxt3 で Vuetify3 を使う もご参考に!)

以下でVuetifyを追加します。

パッケージの追加

$ yarn add vuetify@next @mdi/font
$ yarn add -D sass

Nuxt用の設定を行います。

nuxt.config.ts
export default defineNuxtConfig({
  css: [
    "vuetify/lib/styles/main.sass",
    "@mdi/font/css/materialdesignicons.min.css",
  ],
  build: {
    transpile: ["vuetify"],
  },
})
plugins/vuetify.ts
import { createVuetify } from "vuetify"
import * as components from "vuetify/components"
import * as directives from "vuetify/directives"

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    components,
    directives,
  })

  nuxtApp.vueApp.use(vuetify)
})

APIプロキシ設定の追加

別立てでAPIサーバーを用意する前提で、開発サーバー(Nitro)にプロキシサーバーを設定します。
※本記事ではSSR非対応です、ゴメンナサイ!

nuxt.config.ts
  ssr: false, // SSRは使わない
  nitro: {
    devProxy: {
      "/api": {
        target: "http://localhost:8000/api", // ポート8000で/api以下を転送する例
      },
    },
  },

上記で、 /api 以下へのHTTPリクエストをAPIサーバーに転送できます。
useRuntimeConfigを使ってuseFetchに渡すURLを切り替えたり、APIサーバー側でCORSの設定を入れる必要が無くなります。

なお、APIサーバーへの転送はこの後でmswを追加するので一旦は使われなくなります。

mswの追加

APIサーバーとは独立でフロントエンドを開発していくために、Mock Service Worker (msw)を使ってAPIのモック化を行います。

まずパッケージを追加します。

$ yarn add -D msw

次に、サンプルとして以下のAPIをモック化します。

  • GET /api/users -> ユーザー一覧を返す
  • GET /api/users/{userId} -> ユーザー詳細を返す

※完全なコードはGitHubリポジトリを参照してください。

mocks/api/getUsers.ts
// ダミーのレスポンスを定義

import { ResponseResolver, restContext, RestRequest } from "msw"

import { User } from "@/assets/models/User"

const get: ResponseResolver<RestRequest, typeof restContext> = (
  _req,
  res,
  ctx
) => {
  return res(
    ctx.status(200),
    ctx.json([
      {
        userId: 1,
        name: "スレッタ・マーキュリー",
      },
      {
        userId: 2,
        name: "ミオリネ・レンブラン",
      },
      {
        userId: 3,
        name: "グエル・ジェターク",
      },
    ] as User[])
  )
}

export default { get }
mocks/api/getUserDetail.ts
// ダミーのレスポンスを定義

import { ResponseResolver, restContext, RestRequest } from "msw"

import { UserDetail } from "@/assets/models/UserDetail"

const get: ResponseResolver<RestRequest, typeof restContext> = (
  req,
  res,
  ctx
) => {
  const userId = parseInt(req.params.userId as string, 10)
  let response: UserDetail
  switch (userId) {
    case 1:
      response = {
        userId: 1,
        name: "スレッタ・マーキュリー",
        studentId: "LP041",
        dormName: "地球寮",
      }
      break
    case 2:
      response = {
        userId: 2,
        name: "ミオリネ・レンブラン",
        studentId: "LS001",
        dormName: "N/A",
      }
      break
    case 3:
      response = {
        userId: 3,
        name: "グエル・ジェターク",
        studentId: "KP001",
        dormName: "N/A",
      }
      break
    default:
      return res(ctx.status(404))
  }
  return res(ctx.status(200), ctx.json(response))
}

export default { get }
mocks/handlers.ts
// URLとの関連付け

import { rest } from "msw"
import getUsers from "@/mocks/api/getUsers"
import getUserDetail from "@/mocks/api/getUserDetail"

export const handlers = [
  rest.get("/api/users", getUsers.get),
  rest.get("/api/users/:userId", getUserDetail.get),
]
mocks/browser.ts
// Service Workerの定義

import { setupWorker } from "msw"
import { handlers } from "./handlers"

export const worker = setupWorker(...handlers)
plugins/msw.ts
// mswの初期化

import { worker } from "@/mocks/browser"

export default defineNuxtPlugin(async () => {
  await worker.start() // setupでfetchするにはawaitが必要
})

public/mockServiceWorker.js というファイルも必要になります。以下のコマンドで作成します。

$ mkdir public
$ npx msw init public/ --save=false

サンプルアプリの実装

mswで設定したAPIを呼び出す簡単なアプリをコーディングします。

pages/
  index.vue        ユーザー一覧(トップページ)
  users/
    [userId].vue   ユーザー詳細(URLは /users/{userId})
pages/index.vue
<template>
  <div>
    <div v-if="pending">Loading...</div>
    <div v-else>
      <v-list>
        <v-list-item
          v-for="user in data"
          :key="user.userId"
          :to="`/users/${user.userId}`"
          >{{ user.name }}</v-list-item
        >
      </v-list>
    </div>
  </div>
</template>

<script setup lang="ts">
import { User } from "@/assets/models/User"

const { pending, data } = useFetch<User[]>("/api/users")
</script>
pages/users/[userId].vue
<template>
  <div>
    <div v-if="pending || !data">Loading...</div>
    <div v-else>
      <div>
        <span>名前: </span><span>{{ data.name }}</span>
      </div>
      <div>
        <span>学籍番号: </span><span>{{ data.studentId }}</span>
      </div>
      <div>
        <span>在籍寮: </span><span>{{ data.dormName }}</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { UserDetail } from "@/assets/models/UserDetail"

// URLからユーザーIDを取得
const route = useRoute()
const userId = route.params.userId

const { pending, data } = useFetch<UserDetail>(`/api/users/${userId}`)
</script>

以下が動作している様子です。
image.png

mswの選択有効化

フロントエンドの開発中はmswによるダミーAPIを使用し、APIサーバーが実装できてきたらそちらと結合して動作できるように、mswをオフにする機能を追加します。

NuxtのRuntime Config機能を使用して、NUXT_PUBLIC_MSW環境変数が設定されている場合のみmswが初期化されるようにします。

nuxt.config.ts
  runtimeConfig: {
    public: {
      msw: "",
    },
  },
plugins/msw.ts
export default defineNuxtPlugin(async () => {
  const config = useRuntimeConfig()
  // 環境変数 NUXT_PUBLIC_MSW が設定されている場合有効化
  if (config.msw) {
    await worker.start() // setupでfetchするにはawaitが必要
  }
})

これで、以下のように実行した場合のみmswが有効になり、通常のyarn devでは本物のAPIサーバーと通信するようになります。

$ NUXT_PUBLIC_MSW=1 yarn dev

まとめ

以上でテンプレートプロジェクトの完成になります。
今後何かあれば更新するかもしれません。

19
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?