2024年 「よりそうお葬式」で対応したパフォーマンス改善のまとめ

現在、[よりそうお葬式](https://www.yoriso.com/sogi/area/)では、Nuxt3を使用した新基盤への移行作業を進めています💪
本記事では、この新基盤ページで今年実施したSEO対策についてまとめていきたいと思います。

# はじめに
よりそうお葬式のページでは、暫定目標として次の状態を目指し、パフォーマンスのチューニングを行っています。

- **PC**: 「不良」「改善が必要」ともに0件
- **SP**: 「不良」0件

まだ完全に目標を達成できていませんが、引き続き試行錯誤を重ねています。本記事では、今年行った主な施策を振り返り、その効果を共有します。

# フロントエンドの構成について
よりそうお葬式のフロントエンドは Nuxt + Vue.jsで作られています。
またUIフレームワークとしてVuetifyを導入して作成しています。


# バンドルサイズ

## バンドルサイズの分析
Nuxt3で用意されている `npx nuxi analyze` を使用してバンドルサイズを可視化し、分析を行いました。

![364710981-c5cea76f-7b50-4e8e-a8c1-4620ba167d5e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134725/09a77d3e-4b89-7d87-4791-3517442f7128.png)

可視化することで、容量が大きなプラグインや、無駄に読み込まれているコンポーネントがないかなど、問題点を洗い出しやすくなります。

## プラグイン削除
プラグインを削減するために下記のような対応を行いました。

- 自前で代替えできるプラグインは削除
- Chart.jsやSwiper.jsなど、リッチなUIを実現するために使用しているプラグインなどはデザインの見直しで削除できないか検討

## Scoped SCSSの廃止
Scoped SCSSを使用していると、`data-v-〇〇=""` という属性が付与されていきます。
下記のルールを徹底するようコードを修正し、Scoped SCSSを削除し、HTMLの容量を削減しました。

```
{Vueで名付けたコンポーネント名}__{Block名}--{modify名}
```

また、ビルドされたソースコード上でコンポーネント名を検索することができるというメリットもあります。

## Vuetifyのtree shakingの活用
[Vuetifyの tree shaking](https://vuetifyjs.com/ja/features/treeshaking/#automatic-treeshaking) 設定を行うことで、importされているコンポーネントのみをバンドルファイルに含めることができます。

## Vuetifyコンポーネントの廃止
独自実装に置き換えた方がバンドルサイズを削減できそうな下記のUIコンポーネントは置き換えを行いました。

- v-btn
- v-card
- v-img

## レスポンシブ実装の見直し
SPとPCでそれぞれ要素を用意して、表示の切り替えを行なっている箇所が散見されていました。デザインと実装の見直しを行い、共通のDOMで実装するように変更し容量の削減を行いました。


```
/** 改善前 **/
<div class="d-pc">
PC用コンテンツ
</div>

<div class="d-sp">
SP用コンテンツ
</div>

/** 改善後 **/
<div>
PC・SP共通のコンテンツ
</div>
```

## バナーの見直し
テキストや画像の組み合わせでデザインされていたバナーは、なるべく1枚の画像に置き換えてDOM容量を削減しました。

## アイコンの見直し
[Material Design Icon](https://fonts.google.com/icons)フォントを使用していましたが、プロジェクト上で使用しているアイコンは限られた数のみでした。

そのため、フォントの使用は廃止し、使用しているアイコンのみSVGで読み込み、容量を削減しています。

## フォントのサブセット化
価格や電話番号など、数字と記号のみを使用する箇所については、[fonttools](https://github.com/fonttools/fonttools) を用いて必要な文字だけを含むフォントファイルをサブセット化しました。

## バンドルサイズの削減結果
上記の施作を行なった結果、プロジェクト全体のバンドルサイズは **792KB → 465KB** まで削減することができました。

| 対応前のバンドルサイズ(792KB) | 現在のバンドルサイズ(465KB) |
| - | - |
| ![364710981-c5cea76f-7b50-4e8e-a8c1-4620ba167d5e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134725/55f72830-87c4-392e-76a2-c040da5571b2.png) | ![スクリーンショット 2024-12-25 20.23.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134725/864db177-7a40-0a62-e029-72c105b9952f.png) |


# 画像
### preload対応
ファーストビューに表示される画像に対して、preloadで対応できるように専用のコンポーネントを作成して対応しました。

preloadに対応させるには`<head>`タグ内に`<link>`を追加する必要があるため、nuxtの[useHead](https://nuxt.com/docs/api/composables/use-head)を使用して実装しました。

(実装例 一部省略しています)
```vue
<script setup lang="ts">
export interface Props {
  /**
   * 画像src
   */
  src: string
  /**
   * プリロード
   */
  preload?: boolean
}
const props = withDefaults(defineProps<Props>(), { eager: false, preload: false })

// 画像をpreload
if (props.preload) {
  const images: string[] = []
  if (props.src)
    images.push(props.src)
  if (props.srcMd)
    images.push(props.srcMd)

  useHead({
    link: images.map(image => ({
      rel: 'preload',
      as: 'image',
      href: image,
      fetchpriority: 'high',
    })),
  })
}
</script>

<template>
  <picture
    class="uiImg"
  >
    <img
      :src="src"
      :alt="alt"
      :loading="eager ? 'eager' : 'lazy'"
    >
  </picture>
</template>
```

### CLS対策
画像の読み込み時に発生するレイアウトシフト（高さが0の状態から、画像の読み込み完了後にずれてしまう現象）を防ぐため、画像コンポーネントにおいて、`aspect-ratio`、`width`、および `height` の指定を必須化しました。

# 結果
## 7月26日時点でのCore Web Vitalの結果
![スクリーンショット 2024-12-25 12.04.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134725/c34d8075-582a-4cdb-9adb-65ac69f6d8ba.png)

この時点では「不良」や「改善が必要」のページがまだ一定数存在しており、目標達成には程遠い状況でした。


## 12月21日時点でのCore Web Vitalの結果
![スクリーンショット 2024-12-25 19.09.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/134725/4b427380-4732-8222-912f-a0ca909fb218.png)

施策を積み重ねることで、全体的に数値は改善しつつありますが、依然として「不良」・「改善が必要」ページの解消にはもう少し時間がかかる見込みです。