🎨

Nuxt 3 で Vuetify 3 を使う

2023/02/13に公開
5

Nuxt 3 で Vuetify 3 を使った開発の始め方をまとめました。

この記事の初稿時点でのバージョンは下記のとおりです。

Nuxt 3 をインストールする

Nuxt 3 を始める際は、つぎのコマンドを利用します。

npx nuxi init プロジェクト名 # フォルダ名になります

そして Nuxt 3 と、そのなかで使われている各種パッケージをインストールします。

cd プロジェクト名
npm install # もしくは yarn install, pnpm install など

無事入ったら、開発サーバーを立ち上げると、サンプルのウェルカムページが立ち上がることでしょう。

npm run dev # もしくは yarn dev, pnpm dev など

デフォルトの場合 http://localhost:3000 で開けます。

Nuxt 3.0.0 がリリースされた際にキャプチャした動画です。
Vite の圧倒的な速さが分かりますね。

Vuetify 3 をセットアップする

Vuetify 3 と sass と Vite のプラグインを入れます。

npm install -D vuetify@next sass vite-plugin-vuetify

Nuxt のプラグインで Vuetify を読み込む

いまのところ、公式の Nuxt モジュールが存在しないため、 Nuxt のプラグインとして Vue で 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({
    ssr: true,
    components,
    directives,
    // 他の設定をここに記述していく
  })

  // Vue.js で Vuetify を使用する
  nuxtApp.vueApp.use(vuetify)
})

createVuetify() の引数として、各種設定を読み込みます。

このままでも良いですが、別で設定ファイルを用意しておき読み込むようにすると管理しやすくなりますので後述します

nuxt.config.ts

nuxt.config.ts で Vuetify の事前ビルドと sass ファイルの読み込みを行います。

先立って次のファイルを用意します。

assets/main.scss
@use "vuetify/styles";

@use "vuetify"; だと、Nuxt 3.1.2 以降でうまく読み込まれないため、直接ファイルを指定しています
@use "vuetify/styles"; に変更されています。

あらためて nuxt.config.ts を記述します。
なお、上述のプラグインファイルは自動的に読み込まれるため、記載しません。

nuxt.config.ts
import { defineNuxtConfig } from 'nuxt/config'
import vuetify from 'vite-plugin-vuetify'

export default defineNuxtConfig({
  build: {
    transpile: ['vuetify'],
  },
  hooks: {
    'vite:extendConfig': (config) => {
      config.plugins!.push(vuetify())
    },
  },
  vite: {
    ssr: {
      noExternal: ['vuetify'],
    },
    define: {
      'process.env.DEBUG': false,
    },
  },
  css: ['@/assets/main.scss'],
})

ここまでで使えるようになったはずです。

app.vue を書き換えて確認してみてください。

app.vue
<template>
  <div>
    <VBtn>Vuetify のボタン</VBtn>
  </div>
</template>

Vuetify の設定をカスタマイズしやすいようにする

Vuetify の各コンポーネントのデフォルト設定を行うファイルを作成します。

helpers/defaults.ts
import { DefaultsInstance } from 'vuetify/lib/framework.mjs'

export const defaults: DefaultsInstance = {
  VAppBar: {
    elevation: 0,
  },
  VBtn: {
    variant: 'flat',
    height: 38,
    rounded: 'lg',
    size: 'small',
  },
  VTextField: {
    color: 'primary',
    variant: 'outlined',
    density: 'comfortable',
  },
}

設定する内容は好みで構いません

つぎに、テーマファイルを作成します。

helpers/themes.ts
import { ThemeDefinition } from 'vuetify'

// テーマ名
export const MAIN_THEME = 'mainTheme'
// Light mode theme
export const mainTheme: ThemeDefinition = {
  dark: false,
  colors: {
    background: '#FFFFFF',
    surface: '#FFFFFF',
    primary: '#4f46e5',
    secondary: '#9333ea',
    error: '#ef4444',
    info: '#3b82f6',
    success: '#22c55e',
    warning: '#f59e0b',
  },
}

// Dark モードのテーマ名
export const MAIN_DARK_THEME = 'mainDarkTheme'
// Dark mode theme
export const mainDarkTheme: ThemeDefinition = {
  dark: true,
  colors: {
    background: '#0C111B',
    surface: '#1f2937',
    primary: '#6366f1',
    secondary: '#9333ea',
    error: '#ef4444',
    info: '#3b82f6',
    success: '#22c55e',
    warning: '#f59e0b',
  },
}

これをプラグインで読み込みます。

plugins/vuetify.ts
import { createVuetify } from 'vuetify'
import { MAIN_THEME, mainTheme, MAIN_DARK_THEME, mainDarkTheme } from '@/helpers/themes'
import { defaults } from '@/helpers/defaults'
import '@mdi/font/css/materialdesignicons.css' // 使用するアイコンを読み込む `mdi-xxx`

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    ssr: true,
    defaults,
    display: {
      mobileBreakpoint: 'sm',
    },
    // add theme
    theme: {
      defaultTheme: MAIN_THEME,
      themes: {
        mainTheme,
        mainDarkTheme,
      },
      // primary-darken-9 primary-lighten-9 までできるようにする
      variations: {
        colors: ['primary', 'secondary', 'accent'],
        lighten: 9,
        darken: 9,
      },
    },
  })

  nuxtApp.vueApp.use(vuetify)
})

必要なアイコンのみ読み込むようにする方法

plugins/vuetify.ts で読み込んでいる @mdi/font/css/materialdesignicons.css は、すべてのアイコンを読み込んでいるため、ファイルサイズが大きくなります。
手元のバージョンで 394KB ありました。

これを必要なアイコンのみ読み込むように変更する対応です。

こちらの記事に書かれている内容をそのまま実装してみました。
(引き続きMaterial Design Iconsを使用した場合の実装例です)

npm install -D @mdi/js

必要なファイルをインストールしたのち plugins/vuetify.ts をつぎのように変更します。

plugins/vuetify.ts
import { createVuetify } from 'vuetify'
+ import { aliases, mdi } from 'vuetify/iconsets/mdi-svg'
import { MAIN_THEME, mainTheme, MAIN_DARK_THEME, mainDarkTheme } from '@/helpers/themes'
import { defaults } from '@/helpers/defaults'
- import '@mdi/font/css/materialdesignicons.css' // 使用するアイコンを読み込む `mdi-xxx`

export default defineNuxtPlugin((nuxtApp) => {
  const vuetify = createVuetify({
    ssr: true,
    defaults,
    display: {
      mobileBreakpoint: 'sm',
    },
+   // add icons
+   icons: {
+     defaultSet: 'mdi',
+     aliases,
+     sets: {
+       mdi,
+     },
+   },
    // add theme
    theme: {
      defaultTheme: MAIN_THEME,
      themes: {
        mainTheme,
        mainDarkTheme,
      },
      // primary-darken-9 primary-lighten-9 までできるようにする
      variations: {
        colors: ['primary', 'secondary', 'accent'],
        lighten: 9,
        darken: 9,
      },
    },
  })

  nuxtApp.vueApp.use(vuetify)
})

これで準備は整いました。

各コンポーネントでは利用するアイコンのみを次のように import したうえで利用します。

components/MyComponent.vue
<script setup lang="ts">
import { mdiPost } from '@mdi/js'
</script>

<template>
  <VIcon :icon="mdiPost" />
</template>

これで適切に Tree Shaking され、ビルド後のファイルサイズは削減されます。

おわりに

以上でセットアップ完了です。

Vuetify 3 は ver 2 と比較して、より簡潔に記述できるようになりました。

記述方法の変更もありますので、しばらくは(英語の)公式ドキュメントを読みながら作業する必要がありそうです。
(機会があればまとめたいと思います)

個人的には、全 Vue.js ユーザーにおすすめな UnoCSS と併用して使っています。
(Tailwind CSS 互換のユーティリティクラスを自動的に生成してくれるので便利です)

また Material Design ではないようですが Anu にも注目しています。
(UnoCSS による UI コンポーネントです)
大抵のコンポーネントはすでにひととおり揃っていますので、よりライトに各種 UI を活用できそうですね。

誤記や間違いを見つけた方へ

ぜひコメントでお知らせくださいませ。
より正確な情報を広めていきたいと思っています。

Discussion

抹茶抹茶

plugins/vuetify.ts の

import '@mdi/font/css/materialdesignicons.css' // 使用するアイコンを読み込む `mdi-xxx`

ですが、

import { aliases, mdi } from 'vuetify/iconsets/mdi-svg';

createVuetify に

    icons: {
      defaultSet: 'mdi',
      aliases,
      sets: {
        mdi,
      },
    },

を追加するとステキだと思いました

ここのをパクっただけですが・・・
https://zenn.dev/ichii731/articles/66b4cf79d2cae6

coedocoedo

おお!そんなことができるようになっていたのですねー
試してみて書き換えます!

コメントありがとうございます 🙂

(2023-02-25: 追記しました!ありがとうございます)

makaaaamakaaaa

とてもわかりやすいです。
Nuxt3でv-deta-tableを実装したいのですが、可能でしょうか。
components:{VDataTable }の記述の仕方ご存知でしたら教えてほしいです。

EVE:/i (いゔ)EVE:/i (いゔ)

app.vueのソースですが、

<template>
  <div>
    <VBtn>Vuetify のボタン</VBtn>
  <div>
</template>

divが閉じていませんでした・・・