Vuetifyとの上手い付き合い方

WEMEX株式会社 Advent Calendar 2023の12日目の記事です。
Vueで利用されるUI Framework [Vuetify](https://vuetifyjs.com/en/)が2023−11にv3.4 (Blackguard)をリリースしました。
Labsだったコンポーネントも大分少なくなり、ある程度安定して使える様になってきた印象です。
そこで、プロダクトでVuetify3を利用するにあたって、こんな調整をすれば使い勝手がよくなりますよ、といったチップスを紹介したいと思います。

# 利用しないコンポーネントをバンドル対象から除外する
Vuetifyでは多様なコンポーネントが用意されていますが、プロジェクトで全てのコンポーネントを利用するケースそうそうないと思います。
使うコンポーネントが限られた状態でいざビルドすると、利用しないコンポーネントも一緒にバンドルされてしまい、結果ファイルサイズが肥大化してしまいます。

```js
// src/plugins/vuetify.ts

// Treeshakingする前
// デフォルトはこの様な形で呼び出していると思います

import 'vuetify/styles'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

const vuetify = createVuetify({
  components,
  directives,
})

export default vuetify
```

その状態を防ぐために Treeshaking（不要コードの除去）する設定を入れましょう。

https://vuetifyjs.com/en/features/treeshaking/

利用するモジュールバンドラに合わせて必要なプラグイン（viteならvite-plugin-vuetify）を導入します。

```js
// vite.config.ts

import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'  // プラグインのimport

export default {
  plugins: [
    vue(),
    vuetify(),  // 追加
  ],
}
```

```js
// src/plugins/vuetify.ts
// Treeshakingした後

import 'vuetify/styles'
import { createVuetify } from 'vuetify'

const vuetify = createVuetify()

export default vuetify
```

このように設定すればビルドしたときに実際に利用しているコンポーネントのみをバンドル対象にすることが可能になります。


# コンポーネントのスタイルを調整
プロダクトで表現するUIデザインはデザイナーと協業しながら作っていくことが多いと思います。
そのため、純粋なVuetifyのコンポーネントをそのまま利用すると言うよりは、微妙な調整をしながらプロダクトにあったコンポーネントへ最適化することが多いのではないでしょうか。

幸いなことにVuetifyの各コンポーネントでは上書き可能なSASS変数を公開しています。
この変数を書き換えることでコンポーネントのスタイルを調整することが可能です。

https://vuetifyjs.com/en/features/sass-variables/#sass-variables

:::note warn
この作業をするにはTreeshakingの設定が済んでいる必要があります
:::

任意のディレクトリにSASS変数を定義するファイルを作成します

```scss
// src/styles/settings.scss

@use 'vuetify/settings' with (
  $button-height: 40px,
);
```
モジュールバンドラの設定ファイルに読み込むSASSファイルを指定します

```js
// vite.config.ts

import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'

export default {
  plugins: [
    vue(),
    vuetify({
      styles: {
        configFile: 'src/styles/settings.scss',  // SASS変数定義ファイルを指定
      }
    }),
  ],
}
```

この状態で `v-btn` を利用するとボタンの高さが40pxになります。
他のSASSに関しても同様にコンポーネントのAPIページを参照しながらスタイルを調整することが可能になります。

ただし、ボタンは直感通りに変更することが可能ですが、中には変更するのに一手間工夫が必要も存在します。


### v-text-fieldの調整
中でも分かりにくいのは `v-text-field` ではないかと思います
（私は結構ハマりました。。。）

デフォルトの高さを狭めたいと思い調整しようとしてもAPIページにそれらしいSASSが公開されておらず。。かと言ってPropsで指定するパターンでもなく。直接スタイルを変更するとデザインが崩れる状態でした。

で、泣く泣くコンポーネントのコード覗いてみると内部では`v-input`と`v-field`が使われていました。

https://github.com/vuetifyjs/vuetify/blob/master/packages/vuetify/src/components/VTextField/VTextField.tsx

つまり、`v-text-field` の調整には `v-input`と`v-field` の変数を調整する必要があったのです！（公式に記載されていないので分かり辛い！）

以下のような形で変数を調整した場合、デフォルトの高さは44pxに変更されます。

```scss
// src/styles/settings.scss

@use 'vuetify/settings' with (
  $input-control-height: 44px,  // inputの高さを指定
  $field-input-padding-top: 8px, // inputのpadding top/bottom を調整
  $field-input-padding-bottom: 8px,
);
```

### SASSがまったく公開されていない場合
それでも調整できそうもない場合はdeepセレクターを利用して直接スタイルを更新します。

https://vuejs.org/api/sfc-css-features.html#deep-selectors

個人的にdeepセレクターは最終手段だと思っています。
実装の内部情報であるクラス名をから辿ることになるめた、バージョンアップなどでクラス名が少しでも変更されるとスタイル適用されません。
そのため、利用する際も必要最低限に留めたほうがよいでしょう。


# コンポーネントのグローバル設定を変更
コンポーネントのデフォルトPropsやスタイルクラスを明示的に指定することができます。

https://vuetifyjs.com/en/features/global-configuration/

```js
// src/plugins/vuetify.ts

import { createApp } from 'vue'
import { createVuetify } from 'vuetify'

export default createVuetify({
  defaults: {
    VBtn: {
      variant: 'outlined'  // v-btnを使った時のデフォルトがoutlinedになる
    },
  },
})
```

この調整を入れることで利用する側で同じ様なPropsやスタイルを適用する必要がなくなります。

プロダクトで利用するコンポーネントのデフォルトの形が見えてきた段階で、適した形に設定すればDXが向上すると思います。
※こいった設定はエンジニアの判断だけで操作するのではなくデザインと認識を合わせながら進めたほうがお互いのためになるのではないかと考えます。（独断で設定しても後から違う！となり得るので。。。）

# まとめ
以上、3つ紹介させていただきました。まとめると

- バンドルモジュールのサイズを最適化するために`Treeshaking`をしよう
- コンポーネントのSASS変数を調整してプロダクトにあったスタイルに整えよう
- コンポーネントの標準的な使い方が見えたらグローバルな設定で調整しよう

になります！
それでは、良きフロントエンド開発を🤟🤟🤟