【Nuxt3 + Vuetify3】インストール 〜 レイアウト作成まで

# 概要

[Nuxt3](https://v3.nuxtjs.org/) と [Vuetify3](https://next.vuetifyjs.com/en/getting-started/installation/) を使用して default レイアウトを作成するまでのメモです:memo:

**ゴール**

![FireShot Capture 008 -  - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488228/a1376747-7392-12e3-4f90-e2186a336061.png)

コード全体: https://github.com/RikuOta/nuxt3-vuetify3-example

**各種バージョン**

- macOS: `Monterey (12.4)`
- Node.js: `18.4.0`
- Yarn: `1.22.19`
- Nuxt3: `3.0.0-rc.4`
- Vuetify3: `3.0.0-beta.4`

:::note warn
Nuxt3 は RC 版ですが、Vuetify3 はベータ版のため実稼働アプリケーションへの導入はまだ難しそうです。

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

> The Vuetify 3 Beta is for testing purposes only and is not intended for production applications.

以下の記載もあるのでもう間もなく:question:

https://vuetifyjs.com/ja/introduction/roadmap/

> 現在開発中のリリースは次のとおりです。
> v3.0 (Titan)
> - Target Release: June 2022

:::

# Nuxt3 をインストール

[Nuxt 3 - Quick Start](https://v3.nuxtjs.org/getting-started/quick-start/)

```sh
$ yarn dlx nuxi init nuxt3-vuetify3-example
$ cd nuxt3-vuetify3-example
$ yarn install
```

# Vuetify3 を導入

[Vuetify3 - Installation](https://next.vuetifyjs.com/en/getting-started/installation/)

## 関連ライブラリをインストール 

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

- `vuetify@next`
    - Vuetify3
- `@mdi/font`
    - Vuetify の `<v-icon>` などで [Material Design Icons](https://materialdesignicons.com/) を使用するために必要
- `sass`
    - .vue ファイル内で `<style lang="scss">` するために必要

現在の `package.json` の状態 :arrow_down:

```package.json
{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview"
  },
  "devDependencies": {
    "@mdi/font": "^6.7.96",
    "nuxt": "3.0.0-rc.4",
    "sass": "^1.53.0",
    "vuetify": "^3.0.0-beta.4"
  }
}
```

## Vuetify プラグインを作成

[Nuxt3 - Plugins directory](https://v3.nuxtjs.org/guide/directory-structure/plugins)

```sh
$ mkdir plugins
$ touch plugins/vuetify.ts
```

```plugins/vuetify.ts
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import { aliases, mdi } from 'vuetify/iconsets/mdi'
import '@mdi/font/css/materialdesignicons.css'

export default defineNuxtPlugin(nuxtApp => {
  const vuetify = createVuetify({
    components,
    directives,
    icons: {
      defaultSet: 'mdi',
      aliases,
      sets: {
        mdi
      }
    }
  })

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

※ Nuxt3 では `plugins/` ディレクトリ内のファイルは自動的に読み込まれます。

## nuxt.config.ts を修正

```nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
  css: [
    'vuetify/lib/styles/main.sass'
  ],
  build: {
    transpile: ['vuetify']
  }
})

```

**動作確認**

```app.vue
<template>
  <v-container>
    <v-btn class="mr-6">
      Normal
    </v-btn>

    <v-btn color="primary">
      Primary
    </v-btn>
  </v-container>
</template>
```

```sh
$ yarn dev -o

Nuxt CLI v3.0.0-rc.4

  > Local:    http://localhost:3002/ 
  > Network:  http://192.168.2.60:3002/
  > Network:  http://169.254.121.79:3002/

ℹ Vite client warmed up in 3281ms
ℹ Vite server warmed up in 713ms
✔ Vite server built in 3791ms
✔ Nitro built in 169 ms 
```

![FireShot Capture 005 -  - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2315561/89d153a4-ec11-8088-091c-62ce9f52993b.png)

問題なさそう！

# 色の設定

アプリ全体で使用する色の設定をします。
[Vuetify3 - Theme configuration](https://next.vuetifyjs.com/en/features/theme/)

```plugins/vuetify.ts
// ※追加分のコードのみ記載しています。

import { createVuetify, ThemeDefinition } from 'vuetify'
import colors from 'vuetify/lib/util/colors'

...

const basicTypographyColor = colors.grey.darken3

const customTheme: ThemeDefinition = {
  dark: false,
  colors: {
    primary: colors.blue.base,
    'on-surface': basicTypographyColor,
    'on-background': basicTypographyColor,
    // 後は必要に応じて
  }
}

export default defineNuxtPlugin(nuxtApp => {
  const vuetify = createVuetify({
    ...

    theme: {
      defaultTheme: 'customTheme',
      themes: {
        customTheme
      }
    }
  })

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

# レイアウトを作成

[Nuxt 3 - Layouts directory](https://v3.nuxtjs.org/guide/directory-structure/layouts/)

```sh
$ mkdir layouts
$ touch layouts/default.vue
```

```layouts/default.vue
<template>
  <v-app>
    <v-container>
      <slot />
    </v-container>
  </v-app>
</template>
```

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

**ページを作成して動作確認**

[Nuxt3 - Pages directory](https://v3.nuxtjs.org/guide/directory-structure/pages)

```sh
$ mkdir pages
$ touch pages/index.vue
```

```pages/index.vue
<template>
  <div>
    <p class="mb-4">Hey!</p>
    <v-btn color="primary">Primary</v-btn>
  </div>
</template>
```

```sh
yarn dev -o
```

![FireShot Capture 006 -  - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2315561/7885ce7b-0c3e-2873-78b3-f0202458bceb.png)

問題なし！

# $C.xxx のように参照できる定数を作成

```sh
$ mkdir common/constants
$ touch common/constants/index.ts
```

```common/constants/index.ts
export const NAVIGATION_ITEMS = [
  {
    title: 'Dashboard',
    path: '/dashboard',
    icon: 'mdi-clipboard-text'
  },
  {
    title: 'Hello',
    path: '/hello',
    icon: 'mdi-human-greeting-variant'
  }
]
```

```sh
$ touch plugins/constants-injection.ts
```

```plugins/constants-injection.ts
import * as C from '~/common/constants'

export default defineNuxtPlugin(nuxtApp => {
  return {
    provide: {
      C
    }
  }
})
```

`<template>` で読み込むなら :arrow_down:

```vue
<template>
  <p>{{ $C.NAVIGATION_ITEMS }}</p>
</template>
```

`<script>` で読み込むなら :arrow_down:

```vue
<script setup lang="ts">
const { $C } = useNuxtApp()

console.log($C.NAVIGATION_ITEMS)
</script>
```

# ヘッダー & ナビゲーションを作成

[Vuetify3 - App bars](https://next.vuetifyjs.com/en/components/app-bars/)
[Vuetify3 - v-app-bar API](https://next.vuetifyjs.com/en/api/v-app-bar/)

[Vuetify3 - Navigation drawers](https://next.vuetifyjs.com/en/components/navigation-drawers/)
[Vuetify3 - v-navigation-drawer API](https://next.vuetifyjs.com/en/api/v-navigation-drawer/)

[Vuetify3 - Lists](https://next.vuetifyjs.com/en/components/lists/)
[Vuetify3 - v-list-item API](https://next.vuetifyjs.com/en/api/v-list-item/)

[Nuxt3 - State Management](https://v3.nuxtjs.org/guide/features/state-management/)

```layouts/default.vue
<template>
  <v-app>
    <!-- ヘッダー -->
    <v-app-bar
      app
      flat
      border
      class="px-md-6"
    >
      <v-app-bar-nav-icon
        @click="switchNavigationDisplay"
        class="mr-md-4"
      />

      <v-app-bar-title class="font-weight-bold">
        Nuxt3 + Vuetify3 Example 🚀
      </v-app-bar-title>

      <v-spacer />

      <v-icon icon="mdi-account" class="mr-1" />
      <p class="font-weight-bold">demo.user</p>
    </v-app-bar>
    <!-- /ヘッダー -->

    <!-- ナビゲーション -->
    <v-navigation-drawer
      v-model="isNavigationShown"
      app
      floating
      class="px-6 py-4"
    >
      <v-list>
        <v-list-item
          v-for="item in $C.NAVIGATION_ITEMS"
          active-color="primary"
          :to="item.path"
          class="py-3"
        >
          <v-icon :icon="item.icon" class="mr-6" />
          <v-list-item-title
            class="font-weight-bold"
            v-text="item.title"
          />
        </v-list-item>
      </v-list>
    </v-navigation-drawer>
    <!-- /ナビゲーション -->

    <v-main>
      <v-container class="pa-6 pa-md-9">
        <slot />
      </v-container>
    </v-main>
  </v-app>
</template>

<script setup lang="ts">
// ナビゲーションの表示・非表示を制御
const isNavigationShown = useState('isNavigationShown', () => true)
const switchNavigationDisplay = () => {
  isNavigationShown.value = !isNavigationShown.value
}
</script>

<style scoped lang="scss">
// .v-list-item がアクティブ時の背景色を消す方法が分からなかったので無理やり対応😭
::v-deep(.v-list-item__overlay) {
  display: none;
}
</style>
```

**ページを作成して動作確認**

```sh
touch pages/dashboard.vue
```

```pages/dashboard.vue
<template>
  <div>
    <p class="mb-10">dashboard.vue</p>

    <v-row>
      <v-col cols="6" md="3" v-for="n of 8" :key="n">
        <v-sheet
          color="grey-lighten-2"
          height="200"
        />
      </v-col>
    </v-row>
  </div>
</template>
```

```sh
yarn dev -o
```

localhost:3000/dashboard :arrow_down:

![FireShot Capture 008 -  - localhost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488228/a1376747-7392-12e3-4f90-e2186a336061.png)

完成です！
コード全体: https://github.com/RikuOta/nuxt3-vuetify3-example

# 参考記事など

**Nuxt3**

- [Nuxt3](https://v3.nuxtjs.org/)
- [Nuxt 3 を今すぐオススメしたい 15 のポイント](https://zenn.dev/ytr0903/articles/d0a91f6180d34e)
- [Nuxt 3がパブリックベータ版になったので新機能の紹介や所感など](https://zenn.dev/miruoon_892/articles/e06b1c7533d754df8a8c)

**Vuetify3**

- [Vuetify3](https://next.vuetifyjs.com/en/)
- [Nuxt3[RC]でVuetify3[Beta]を使える様にする！マテリアルデザインアイコンの有効化と、カスタムテーマ作成も](https://zenn.dev/tempra/articles/a8405c5a5fe448)
- [How to use Vuetify with Nuxt 3](https://codybontecou.com/how-to-use-vuetify-with-nuxt-3.html)