Nuxt3 × Vuetify3 × TypeScript × SCSS × ESLintでプロジェクトをはじめるために

# はじめに
こんにちは、GxPの森下です！
この記事は[グロースエクスパートナーズ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/gxp)の21日目の記事です。

案件でNuxt2を触ることになったので、勉強も兼ねて新しいNuxt3も触ってみたいと思い、進めていくうちに学んだことをまとめました。

※この記事では、Nuxt3の環境構築など基本的な内容が主であり、Vue3の構文についてはほとんど触れないため、ご了承ください。Vue3の詳細については、[公式ドキュメント](https://ja.vuejs.org/guide/essentials/application.html)を参考にしてみてください。

# 環境

**バージョン**
- OS : Windows11
- Node.js : 20.10.0
- npm : 10.2.3
- npx : 10.2.3
- Nuxt : 3.8.2

# Nuxt3
NuxtはVueベースのフレームワークです。

https://nuxt.com/docs/getting-started/installation


**プロジェクトの雛形の作成**

```bash:bash
npx nuxi@latest init <project-name>
# または
npx nuxi init <project-name>
```

Nuxtのバージョンを指定したい場合は、`nuxi@3.9.1`のように書くことも出来ます。
※この時、プロジェクト名<project-name>を記載しないと自動的にプロジェクト名は`nuxt-app`となります。
上記コマンドを実行後、nodeを使用しているため`npm`を選択。(依存関係のインストールに数分かかります。)

以下のコマンドでアプリを起動出来ます。
```bash:bash
# ディレクトリを移動
cd <project-name>

# 起動
npm run dev -- -o
# または
npm run dev
```
※`npm run dev`はアプリの起動のみで、新規タブは自動で開かれません。そのため、`http://localhost:3000`に自分でアクセスする必要があります。

Vue3には、Option APIとComposition APIの2つの選択肢がありますが、この記事ではComposition APIを使用して記載します。

## Auto-imports

https://nuxt.com/docs/guide/concepts/auto-imports

Nuxt3にはAuto-importsが備わっており、この機能によって[Vue.js APIs](https://vuejs.org/api)やプロジェクトの[components/](https://nuxt.com/docs/guide/directory-structure/components)、[composables/](https://nuxt.com/docs/guide/directory-structure/composables)、[utils/](https://nuxt.com/docs/guide/directory-structure/utils)ディレクトリ配下のものを明示的にimportせずに使用することが可能になっています。

## 拡張機能

https://marketplace.visualstudio.com/items?itemName=Vue.volar

Vue3では上記の拡張機能 (Volar) を入れることで、シンタックスハイライトなどを行ってくれるようになります。[公式ドキュメント](https://ja.vuejs.org/guide/scaling-up/tooling#ide-support)にも記載がありますが、Vue3のプロジェクトでは、Vue2用の拡張機能である[Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur)は無効にするようにしてください。

# Vuetify3

Vuetify3はVueのUIライブラリです。

https://vuetifyjs.com/en/getting-started/installation/#installation

https://nuxt.com/modules/nuxt-vuetify


## Vuetify3の準備

**インストール**

```bash:bash
npm install -D @invictus.codes/nuxt-vuetify
```

**プロジェクト直下に作成された`nuxt.config.ts`に以下を追加**


```ts:nuxt.config.ts
export default defineNuxtConfig({
    modules: [
      '@invictus.codes/nuxt-vuetify'
    ],
})
```

使用する際は、以下の`<v-btn>`のようにVuetify3の`components`を`v-app`タグで囲む必要があります。


```vue:index.vue
<template>
  <v-app>
    <v-btn>Button</v-btn>
    <v-btn icon="mdi-account"></v-btn>
  </v-app>
</template>
```
※`v-btn`では上記のようにタグ内でiconを使用することが出来ます。その際は、`mdi-`に続くiconを使用することになり、以下のサイトのMDI - Icon searchから使用可能なiconを検索することが出来ます。

https://vuetifyjs.com/en/features/icon-fonts/#mdi-icon-search


以上でVuetify3の準備は完了です。Vuetify3の以下の機能について紹介します。
1. [Theme](#1-theme)
2. [Wireframes](#2-wireframes)
3. [Styles and animations](#3-styles-and-animations)
4. [ComponentsとAPI](#4-componentsとapi)

### 1. Theme
Vuetify3をのThemeを使用することで簡単にカラーテーマを設定・変更することができます。

https://vuetifyjs.com/en/features/theme/#api

Nuxt3でThemeを使用する際には、まずpluginを追加する必要があります。
プロジェクト直下に`plugins/vuetify.ts`というファイルを追加し、以下の内容を記載します。

```ts:plugins/vuetify.ts
import { createVuetify } from 'vuetify'

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    theme: {
      defaultTheme: 'light'
    }
  })

  nuxtApp.vueApp.use(vuetify)
})
```

デフォルトのテーマを設定するのに必要なのはこれだけです。デフォルトでは、`light`と`dark`の2種類が用意されています。

テーマを切り替えるボタンを実装したい場合は、[公式ドキュメント](https://vuetifyjs.com/en/features/theme/#changing-theme)にもあるように以下のようなコードを記載します。

```vue:index.vue
<template>
  <v-app>
    <v-container>
      <v-btn @click="toggleTheme">Toggle Theme</v-btn>
    </v-container>
  </v-app>
</template>

<script setup lang="ts">
  import { useTheme } from 'vuetify'

  const theme = useTheme()

  function toggleTheme() {
    theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
  }
</script>

```

ここでは記載しませんが、カスタムテーマを作成し、そのテーマを適用することも可能です。
※上記を行う際、Nuxt3のデフォルトでは`plugins`の直下のファイルしかpluginとして追加されないため、更に下の階層のファイルを使用したい場合は別途`nuxt.config.ts`ファイルに書き込む必要があります。以下に[公式ドキュメント](https://nuxt.com/docs/guide/directory-structure/plugins)の例を示します。

```ts:nuxt.config.ts
export default defineNuxtConfig({
  plugins: [
    '~/plugins/bar/baz',
    '~/plugins/bar/foz'
  ]
})

```

### 2. Wireframes
Vuetify3では簡単にレイアウトを作成することができ、Wireframeとしてサンプルがいくつか用意されています。

https://vuetifyjs.com/en/getting-started/wireframes/#examples

例えば、Discordという名前で以下のようなWireframeが用意されており、右下にあるGitHubに飛ぶことで実際のコードを確認することが出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/cc9c1113-b8c8-6d43-1c37-dcd4ce73b32c.png)

### 3. Styles and animations
Vuetify3にはスタイルの機能も備わっており、BootstrapやTailwind CSSのようにclassに指定することで簡単にスタイルを適用することが出来ます。大きく分けて以下の４つがあります。
- [CSS Reset](#css-reset)
- [Transitions](#transitions)
- [Material color pallette](#material-color-pallette)
- [UTILITY CLASSES](#utility-classes)

<br>

#### CSS Reset
Vuetify3では、デフォルトでリセットCSSが適用されています。

https://vuetifyjs.com/en/styles/css-reset/

ドキュメントにも記載がありますが、リセットCSSを無効化する場合、一部のスタイルには個別にリセットCSSを適用する必要があるようです。

<br>

#### Transitions
Vuetify3のTransitionsの機能を使用することで簡単に動きを付けることが出来ます。

https://vuetifyjs.com/en/styles/transitions/

Transitionsにはいくつか種類がありますが、その使用方法としては大きく分けて2つあります。
ここでは`v-fade-transition`を例に説明します。

<br>

まず、1つ目は`v-menu`のように`props`に`transition`がある場合です。
`v-menu`の説明のページを見てみると`props`内に`transition`が存在することが分かります。


https://vuetifyjs.com/en/api/v-menu/#links


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/9121892a-96fc-20f0-1a4c-2af45bfaacf1.png)


このように`transition`がある場合は、以下の`<v-menu transition="fade-transition">`のように`transition`を指定することが出来ます。

```vue:index.vue
<template>
  <div class="text-center">
    <v-menu transition="fade-transition">
      <template v-slot:activator="{ props }">
        <v-btn
          dark
          color="primary"
          v-bind="props"
        >
          Fade Transition
        </v-btn>
      </template>
      <v-list>
        <v-list-item
          v-for="n in 5"
          :key="n"
        >
          <v-list-item-title v-text="'Item ' + n"></v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>
```

<br>

2つ目は、`v-card`のように`props`に`transition`がない場合です。

https://vuetifyjs.com/en/api/v-card/#links


`transition`がない場合は、対象箇所を`<v-fade-transition>`タグで囲う必要があります。

```vue:index.vue
<template>
  <v-row justify="center" style="min-height: 160px">
    <v-col class="shrink">
      <v-btn class="ma-2" color="primary" @click="expand = !expand">
        Expand Transition
      </v-btn>

      <v-fade-transition>
        <v-card
          v-show="expand"
          height="100"
          width="100"
          class="mx-auto bg-secondary"
        ></v-card>
      </v-fade-transition>
    </v-col>
  </v-row>
</template>

<script>
  export default {
    data: () => ({
      expand: false,
      expand2: false,
    }),
  }
</script>
```

<br>

#### Material color pallette
Vuetify3には、Material colorsというものがあり、以下のように`class`名に`bg-`や`text-`の後ろに続けて用意されている色の名前を加えることで簡単に色を設定することが出来ます。

https://vuetifyjs.com/en/styles/colors/#classes

```vue:index.vue
<template>
  <div class="bg-light-blue-darken-4 text-orange-darken-3 text-center">
    <span>sample text</span>
  </div>
</template>
```

上記のコードで、以下のようなスタイルが適用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/81b3f087-a70d-bafe-5fa6-0953da5ff557.png)

<br>

#### UTILITY CLASSES
Vuetify3では、基本的なスタイルが提供されており、`class`名にそれを追加することでスタイルを適用することが出来ます。
ここではFlexを例に説明していきます。

https://vuetifyjs.com/en/styles/flex/

```vue:index.vue
<template>
  <div class="d-flex justify-center bg-surface-variant">
    <v-sheet
      v-for="n in 3"
      :key="n"
      class="ma-2 pa-2"
    >
      justify-center
    </v-sheet>
  </div>
</template>
```

上記のコードで、以下のようなスタイルが適用できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/d006ed73-00ed-14f0-4f49-8662aaf14b2d.png)

<br>


### 4. ComponentsとAPI
各ComponentにはAPIが用意されており、このAPIにはいくつか機能があるので、そのうち3つを紹介します。
- [Props](#props)
- [Events](#events)
- [Slots](#slots)

https://vuetifyjs.com/en/components/all/#containment

ここでは、`<v-data-table>`を例にComponentsとそのAPIの使い方について説明します。

https://vuetifyjs.com/en/components/data-tables/basics/#usage


https://vuetifyjs.com/en/api/v-data-table/#links

<br>

#### Props
Propsは、`<v-data-table>`に指定された形式でデータを渡すことで、そのデータを使用して`Component`を作成してくれます。
以下は[公式ドキュメント](https://vuetifyjs.com/en/components/data-tables/basics/#usage)の例で、`:items="items"`で`<v-data-table>`にデータを渡していることが分かると思います。

```vue:index.vue
<template>
  <v-data-table :items="items"></v-data-table>
  <v-data-table :items="items" /> <!-- Slotsを用いない場合などは、このように書くことも出来ます -->
</template>

<script setup>
  const items = [
    {
      name: 'African Elephant',
      species: 'Loxodonta africana',
      diet: 'Herbivore',
      habitat: 'Savanna, Forests',
    },
    // ... more items
  ]
</script>
```
※`:item`が`<v-data-table>`側で指定されているPropsの名前で、Propsには`: (v-bind)`をつける時とつけないときがあり、静的な値には`:`はつけませんが、上記のように動的な値には`:`をつけます。

https://ja.vuejs.org/api/built-in-directives.html#v-bind

#### Events
Eventsでは、[ComponentのEvent](https://ja.vuejs.org/guide/components/events)を受け取ることが出来ます。どんな時のEventをどのように受け取れるのかは[APIのページ](https://vuetifyjs.com/en/api/v-data-table/#events)に記載されています。Eventは`$event`で受け取ることが出来ます。言葉だけだと分かりづらいと思うので、以下に例を示します。

```vue:index.vue
<template>
    <v-data-table update:page="func($event)">
        <!-- 省略 -->
    </v-data-table>
</template>
```
上記のようにすることで、関数`func()`に受け取ったEventを引数として渡すことが出来ます。

#### Slots
Slotsでは、指定した箇所の値を書き換えたりすることが出来ます。Slotsは、`v-slot:`または`#`を使って指定することが出来ます。

https://vuetifyjs.com/en/components/data-tables/basics/#header-slot

以下の例では、書き換える場所として`header.id`を指定しており、後ろの`="{ column }"`で変数を与えています。この場合、指定しているものが`header.id`のため、参照するものは公式ドキュメントのうち、[`header.${string}`](https://vuetifyjs.com/en/api/v-data-table/#slots-[%60header.{string}%60]) とさらにその中の`column: InternalDataTableHeader`になります。この[`InternalDataTableHeader`](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDataTable/types.ts#L32-L41)の中を見てみると[`DataTableHeader`](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDataTable/types.ts#L10-L30)を[Omit](https://typescriptbook.jp/reference/type-reuse/utility-types/omit)していることが分かると思います。
なので、`InternalDataTableHeader`は、`DataTableHeader`の一部を書き換えたものであり、以下のように`{{ column.title.toUpperCase() }}`とすることで`id`の`header`の`title` (以下の場合、`items`の`key`がそのまま`header`の`title`になるため`id`という文字) を大文字に変換することが出来るようなります。

```vue:index.vue
<template>
  <v-data-table :items="items">
    <template #header.id="{ column }">
      {{ column.title.toUpperCase() }}
    </template>
  </v-data-table>
</template>
<script setup>
  const items = [
      {
        id: 1,
        name: 'T-Shirt',
        size: 'M',
        color: 'Red',
        price: 19.99,
        quantity: 10
      },
      // ... more items
  ]
</script>
```
※ただし、上記のように`#header.id="{ column }"`と書いてしまうとESLintを設定した際に`'v-slot' directive doesn't support any modifier.`というエラーが出てしまうため、``<template #[`header.id`]="{ column }">``のように記載する必要があります。

<br>

公式ドキュメント以外に、[GitHub](https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VDataTable/VDataTable.tsx)も参考になると思いますので、余裕があれば覗いてみてください。

# TypeSctipt
Nuxt3にTypeScriptを導入する方法についてです。

https://nuxt.com/docs/guide/concepts/typescript

## TypeSctiptの準備

**インストール**

```bash:bash
npm i -D typescript vue-tsc
```

**`nuxt.config.ts`に以下を追加**


```ts:nuxt.config.ts
export default defineNuxtConfig({
  typescript: {
    shim: false,       // Volar(拡張機能)を使用する場合はfalse
    strict: true,      // 厳密な型チェックを有効にする
    typeCheck: true    // 開発時の型チェックを有効にする(ビルドパフォーマンスを最適化するためデフォルトでは型チェックが行われない)
  }
})
```

設定の詳細については、以下を参照してください。

https://nuxt.com/docs/api/nuxt-config#builddir

`.vue`ファイルでTypeScriptを有効にする場合は、`lang="ts"`を記載する必要があります。

```vue:index.vue
<script setup lang="ts"></script>
```

<br>

基本的なTypeScriptの書き方についてはここでは割愛しますが、`ref`と`Ref`についてのみ簡単に紹介しようと思います。
以下の内容は[公式ドキュメント](https://ja.vuejs.org/guide/typescript/composition-api.html)の内容を参考にしたものです。

```vue:index.vue
<script setup lang="ts">
  import { ref } from 'vue'
  import type { Ref } from 'vue'

  const year = ref<string | number>('2023')
  const month: Ref<string | number> = ref('12')

  year.value = 2023 // ok!
  month.value = 12  // ok!
</script>

```

上記のように`ref`に直接、型を指定する事もできますが、`Ref`を使用することで明示的に型を指定する事もできます。
※`ref`は初期値を設定し、型を記載しない場合は、型推論が行われます。


# SCSS
Nuxt3にSCSSを導入する方法についてです。

https://nuxt.com/docs/getting-started/styling

## SCSSの準備
**インストール**


```bash:bash
npm i -D sass
```

`.vue`ファイルでSCSSを有効にする場合は、`lang="scss"`を記載する必要があります。


```vue:index.vue
<style lang="scss"></style>
```


# ESLint

ESLintは、ECMAScriptのLintで、コーディング規約に準じているかの確認を行ってくれるものです。

プロジェクト直下に`.eslintrc`というファイルを作成し、各設定を記載していきます。

<br>

全体像をつかむために以下に概要を示します。


```.eslintrc
{
  "root": true,       // プロジェクトの直下のeslintのファイルの場合に記載
  "env": {            // 環境変数の設定
    "browser": true,  // ブラウザ上で開発を行う場合に記載
    "node": true, 　  // nodeを使用する場合に記載
    "es2021": true,   // ここを記載する場合、以下の"ecmaVersion"は記載する必要はない
  },
  "parser": ,         // ESLintに構文を理解させるために記載
  "parserOptions": {
    "parser": ,              // parserが複数ある場合に記載
    "ecmaVersion": "latest", // ECMAScriptのバージョンを記載
    "sourceType": "module",　// import/exportを使用する場合に記載(デフォルトではscript)
    "ecmaFeatures": {
      "jsx": true,　         // jsx記法を使用する場合に記載
    },
  },
  "extends": [],  // 外部のルールセットを使用する場合に記載
  "plugins": [],  // pluginを使用する場合に記載
  "rules": {      // 個別にルールを定義する場合にこの中に記載
    "quotes": ["error", "single"]  // 「シングルクォートに統一する、違うものを使用したらエラーを出力する」という設定
  },
}
```

**root**

下の階層には別の設定を使用したい場合、その階層ごとにESLintのファイルを作成することが出来ます。

起動の際、ESLintは上の階層のESLintのファイルをマージしていき、`"root": true`が記載されているファイルにたどり着くまで行われます。

内容が競合した場合は、下の階層のものが優先されます。

<br>

**extends**

ルールを`rules`で1つずつ設定していくのは面倒なため、基本的には外部で用意されているものを使用し、個別で対応したいもののみを`rules`に記載していくやり方が一般的だと思います。
 
<br>

※ESLintには上記で使用したファイル形式以外にも、`JS`や`JSON`, `YAML`等、複数のファイル形式が用意されています。異なるファイル形式のESLintのファイルが同じ階層にあった場合は、ファイル形式によって優先度が異なるため注意が必要です。


## Nuxt3でESLintを使うための設定
 
https://nuxt.com/modules/eslint

### `@nuxtjs/eslint-module`の使用方法
**インストール**

```bash:bash
npm install -D eslint @nuxtjs/eslint-module
```

**`nuxt.config.ts`に以下を追加**

```ts:nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/eslint-module'
  ]
})
```


オプションを設定することも出来ます。
オプションのデフォルト値は上記の[公式ドキュメント](https://nuxt.com/modules/eslint)を参照してください。


<br>

起動時に以下のような`lintOnStart`を`false`にするように促す警告が出ます。
実行が出来なくなるわけではないですが、`false`にしないと起動時に時間がかかる場合があります。
```bash:Terminal
warning: LintOnStart is turned on, and it will check for all matching files.                    
```

`lintOnStart`を`false`にするには以下のように記載します。
```ts:nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    ['@nuxtjs/eslint-module', {lintOnStart: false}]
  ]
})
```


## ESLintのルールの適用
### Nuxt ESLint packages (Nuxt2で使用)

https://github.com/nuxt/eslint-config

:::note warn
このESLintのパッケージはNuxt2で使用されるものらしく、誤った内容を記載していました。こちらの記事を参考にされた方、申し訳ありません。この内容については、[こちらのページ](https://github.com/nuxt/eslint-config/issues/311)を参考にしてみてください。Nuxt3で使用するパッケージについては、後述する`nuxt/eslint-config`で記載していますので、そちらをご覧ください。
(以下の内容はNuxt3で使用される`@nuxt/eslint-config`と比較できるように残しておきます。`nuxt`の後ろに`js`がつかないものがNuxt3用なので、使用する際は注意してください。)
:::

このルールは追加設定なしですぐに使え、ページやコンポーネントなどに対するNuxt固有のルールが含まれています。設定できるものとしては以下の2つがあります。

- `@nuxtjs/eslint-config`
- `@nuxtjs/eslint-config-typescript`

上記の2つには以下のものが含まれています。
- [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser)
    - Vueの構造を解析させるために必要なものです。
- [eslint-plugin-vue](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention)の`"plugin:vue/vue3-recommended"`
    - `"plugin:vue/vue3-recommended"`はより多くのルール (PriorityA-C) を含んでおり、Vue3において推奨されるルールが適用されています。
- [typescript-eslint](https://typescript-eslint.io/getting-started/) (`@nuxtjs/eslint-config-typescript`のみ)
    - `@typescript-eslint/eslint-plugin`には100以上のルールが含まれており、TypeScriptのコードに特化したベストプラクティス違反やバグ、スタイル上の問題を検出してくれます。


#### `@nuxtjs/eslint-config-typescript` (`@nuxtjs/eslint-config`) の使用方法
**インストール**

```bash:bash
npm i -D @nuxtjs/eslint-config-typescript
```
 

**`.eslintrc`に以下を追加**


```.eslintrc
{
    "extends": [
      "@nuxtjs/eslint-config-typescript"
    ]
}
```
TypeScriptを使用しない場合は、`@nuxtjs/eslint-config-typescript`を`@nuxtjs/eslint-config`に置き換えてインストール・追加してください。

### Nuxt ESLint packages (Nuxt3で使用)

https://github.com/nuxt/eslint-config

こちらのパッケージにも`@nuxtjs/eslint-config-typescript`と同様に以下のものが含まれています。
- [vue-eslint-parser](https://github.com/vuejs/vue-eslint-parser)
- [eslint-plugin-vue](https://eslint.vuejs.org/rules/#priority-a-essential-error-prevention)の`"plugin:vue/vue3-recommended"`
- [typescript-eslint](https://typescript-eslint.io/getting-started/)

#### `@nuxt/eslint-config` の使用方法
**インストール**

```bash:bash
npm i -D @nuxt/eslint-config
```
 

**`.eslintrc`に以下を追加**


```.eslintrc
{
    "extends": [
      "@nuxt/eslint-config"
    ]
}
```


## 最終的な`.eslintrc`ファイル

各設定やルールの適応を行った`.eslintrc`ファイルは以下のようになります。

```.eslintrc (Nuxt2の場合)
{
  "root": true,
  "env": {
    "browser": true,
    "node": true,
    "es2021": true
  },
  "extends": [
    "@nuxtjs/eslint-config-typescript"
  ],
  "parserOptions": {
    "sourceType": "module"
  }
}
```
:::note warn
こちらもESLintのパッケージと同様にNuxt2のものを記載していたため、Nuxt3を使用する方は以下のものを使用してください。(上記の内容もNuxt3で使用される`@nuxt/eslint-config`と比較できるように残しておきます。)
:::

```.eslintrc (Nuxt3の場合)
{
  "root": true,
  "extends": [
    "@nuxt/eslint-config"
  ],
  "parserOptions": {
    "sourceType": "module"
  }
}
```

※`@nuxt/eslint-config`には、Nuxt2用の`.eslintrc`で設定したenvの内容はすでに含まれているため、記載していません。(ECMAScriptのバージョンは、es6が設定されています。)

上記でESLintの準備が整ったので、ここからはESLintの内容を元にコードを整形する方法について説明します。以下では、基本的なコード整形はPrettierで行い、その後ESLintで整形するという流れを設定していきます。拡張機能をインストール後、`settings.json`で設定を行います。


## 拡張機能のインストール

### Prettier
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/6673c5dc-6e9d-6c1c-4472-0cd7cf65102b.png)

https://github.com/prettier/prettier-vscode

### ESLint
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/90f26722-36e9-3971-f2ff-18124be95423.png)

https://github.com/microsoft/vscode-eslint/

## VSCodeの設定
`settings.json`に以下を記載します。
```json:settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
}
```
※`settings.json`は`Ctrl + Shift + P`でコマンドパレットを開き、`settings.json`で検索することで以下のようにして開くことが出来ます。この際、`既定の設定(JSON)`ではなく、`ユーザー設定を開く(JSON)`ことに注意してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/dd06358b-51b4-c146-f9d7-a3a74def832d.png)


上記の設定で、保存時にコードを整形することが出来るようになります。
※上記の設定に加え、`"editor.formatOnSave": true`を指定するとうまく整形されないことがあるため記載していません。


## おまけ
ESLintについて調べている際に、よく見かけたものの自分は使用しなかったものについて参考程度に記載しておきます。

1. [eslint-plugin-nuxt](#1-eslint-plugin-nuxt)
2. [eslint-plugin-prettier](#2-eslint-plugin-prettier)
3. [babel/eslint-parser](#3-babeleslint-parser)

### 1. eslint-plugin-nuxt

https://github.com/nuxt/eslint-plugin-nuxt

`"plugin:nuxt/recommended"`には[公式ドキュメントのルール](https://github.com/nuxt/eslint-plugin-nuxt#bulb-rules)がすべて含まれており、一貫性を確保するためのデフォルトを強制するルールが適用されます。


#### `eslint-plugin-nuxt`の使用方法
**インストール**

```bash:bash
npm i -D eslint-plugin-nuxt
```

**`.eslintrc`に以下を追加**

```.eslintrc
{
  "extends": [
    "plugin:nuxt/recommended"
  ]
}
```

### 2. eslint-plugin-prettier

https://github.com/prettier/eslint-plugin-prettier

Prettierをあたかもリンターのルールのように動作させるプラグインらしいですが、現在は非推奨となっています。

#### `eslint-plugin-prettier`の使用方法
**インストール**

```bash:bash
npm i -D eslint-plugin-prettier
npm i -D -E prettier
npm i -D eslint-config-prettier
```

**`.eslintrc`に以下を追加**


```.eslintrc
{
  "extends": ["plugin:prettier/recommended"]
}
```

以下に非推奨となっている旨が記載されていますので気になる方はご覧ください。

https://prettier.io/docs/en/integrating-with-linters.html#notes

### 3. babel/eslint-parser

https://www.npmjs.com/package/@babel/eslint-parser

babelはES5以降のコードをそれ以前のものに変換できるツールで、`babel/eslint-parser`はBabelによって変換されたソースコード上で ESLint を実行できるようにするものです。

#### `babel/eslint-parser`の使用方法

**インストール**

```bash:bash
npm i -D @babel/core @babel/eslint-parser
```

**`.eslintrc`に以下を追加**


```.eslintrc
{
  "parser": "@babel/eslint-parser",
}
```

# 実際に作ってみる
以下では、サンプルとして`Header`, `Side Navigation`, `Footer`, `Main`の要素からなるプロジェクトを作ってみます。

## app.vue
`app.vue`はNuxtにおけるメインのコンポーネントになります。
プロジェクト直下にある`app.vue`を以下のものに書き換えます。

```vue:app.vue
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
```

`<NuxtLayout>` : layoutsディレクトリの内容を表示させるためのタグです。
`<NuxtPage />` : pagesディレクトリの内容を表示させるためのタグです。

 
**備考**

- Nuxtではディレクトリが重要な役割を果たしますが、以下の3つがあればとりあえずは問題ないかと思います。他のディレクトリについては興味があれば調べてみてください。

- Nuxt3の雛形作成直後は、以下のディレクトリ (`layouts`, `components`, `pages`) は存在しないため、プロジェクト直下に作成する必要があります。

- 以下の使用されていない、`<script lang="ts" setup></script>`や`<style lang="scss"></style>`は削除しても問題ありませんが、記載出来ることを明示するために残してあります。


## layoutsディレクトリ
レイアウトを作成するためのディレクトリです。

```vue:layouts/default.vue
<template>
  <v-app>
    <DefaultHeader />
    <DefaultSideNav />
    <v-main class="d-flex align-center">
      <v-container>
        <slot />
      </v-container>
    </v-main>
    <DefaultFooter />
  </v-app>
</template>
<script setup lang="ts"></script>
<style lang="scss"></style>
```
※`v-app`はVuetify3を使用するために必要なタグになり、`v-`からはじまるタグはVuetify3のタグになります。

## componentsディレクトリ

コンポーネントを作成するためのディレクトリです。

### Header

```vue:components/DefaultHeader.vue
<template>
  <v-app-bar color="primary" density="compact">
    <template #prepend>
      <v-app-bar-nav-icon />
    </template>

    <v-app-bar-title>Nuxt3 Tutorial</v-app-bar-title>

    <template #append>
      <v-btn icon="mdi-dots-vertical" />
    </template>
  </v-app-bar>
</template>
<script setup lang="ts"></script>
<style lang="scss"></style>
```


https://vuetifyjs.com/en/components/app-bars/#scroll-behavior

### Side Navigation

```vue:components/DefaultSideNav.vue
<template>
  <v-card>
    <v-navigation-drawer
      v-model="drawer"
      :rail="rail"
      permanent
      @click="rail = false">
      <v-list-item
        prepend-avatar="https://randomuser.me/api/portraits/men/85.jpg"
        title="John Leider"
        nav>
        <template #append>
          <v-btn
            variant="text"
            icon="mdi-chevron-left"
            @click.stop="rail = !rail"></v-btn>
        </template>
      </v-list-item>

      <v-divider></v-divider>

      <v-list density="compact" nav>
        <v-list-item
          prepend-icon="mdi-home-city"
          title="Home"
          value="home"></v-list-item>
        <v-list-item
          prepend-icon="mdi-account"
          title="My Account"
          value="account"></v-list-item>
        <v-list-item
          prepend-icon="mdi-account-group-outline"
          title="Users"
          value="users"></v-list-item>
      </v-list>
    </v-navigation-drawer>
  </v-card>
</template>
<script setup lang="ts">
import { ref } from 'vue'

const drawer = ref<boolean>(true)
const rail = ref<boolean>(true)
</script>
<style lang="scss"></style>
```


https://vuetifyjs.com/en/components/navigation-drawers/#expand-on-hover


※`v-layout`は固定の領域を作成するためのものです。主に配置される場所が固定されているものに対して使用します。


### Footer

```vue:components/DefaultFooter.vue
<template>
  <v-footer class="d-flex flex-column justify-end pa-0" app>
    <div class="bg-teal d-flex w-100 align-center px-4">
      <strong>Get connected with us on social networks!</strong>

      <v-spacer />

      <v-btn
        v-for="icon in icons"
        :key="icon"
        class="mx-4"
        :icon="icon"
        variant="plain"
        size="small" />
    </div>

    <div class="px-4 py-2 bg-black text-center w-100">
      {{ new Date().getFullYear() }} — <strong>Vuetify</strong>
    </div>
  </v-footer>
</template>
<script lang="ts" setup>
import { ref } from 'vue'

const icons = ref<string[]>([
  'mdi-facebook',
  'mdi-twitter',
  'mdi-linkedin',
  'mdi-instagram'
])
</script>
<style lang="scss"></style>
```

https://vuetifyjs.com/en/components/footers/#indigo-footer

## pagesディレクトリ
実際に画面に表示されるページを作成するためのディレクトリです。

```pages/index.vue
<template>
  <v-sheet
    elevation="12"
    max-width="600"
    rounded="lg"
    width="100%"
    class="pa-4 text-center mx-auto">
    <v-icon class="mb-5" color="success" icon="mdi-check-circle" size="112" />

    <h2 class="text-h5 mb-6">You reconciled this account</h2>

    <p class="mb-4 text-medium-emphasis text-body-2">
      To see a report on this reconciliation, click
      <a href="#" class="text-decoration-none text-info"
      >View reconciliation report.</a
      >

      <br />

      Otherwise, you're done!
    </p>

    <v-divider class="mb-4" />

    <div class="text-end">
      <v-btn
        class="text-none"
        color="success"
        rounded
        variant="flat"
        width="90">
        Done
      </v-btn>
    </div>
  </v-sheet>
</template>
<script setup lang="ts"></script>
<style lang="scss"></style>
```

※ここで作成したファイル名がそのままパス名になります。`index.vue`は`/` ([http://localhost:3000](http://localhost:3000)) になります。

以下を参照

https://vuetifyjs.com/en/components/sheets/#reconcile-notification

## 完成画面
上記を組み合わせていくと以下のような画面になります。実際のコードは[GitHub](https://github.com/n-morishita/nuxt3-tutorial)の方にもあげてあるので、気になる方は確認してみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3588774/421d87f6-756c-b0b8-6eae-c6909f86c87b.png)


# さいごに

最後までご覧いただきありがとうございました。
これからNuxt3を学び始める人や学び始めたばかりの人のお役に立てれば幸いです。