Closed7

フロントエンドのパフォーマンス改善 Vue3/Nuxt/Vite

ebi_yuebi_yu

proxサーバ(nginx)の対応

proxyサーバとしてnginxを使っていたので、その設定を最適化した。

nginxのgzip対応

gzipによるネットワーク通信ファイルの圧縮を行った。
これが、パフォーマンス改善に一番効いたと思う。

nginx.conf

server {
    gzip on;
    gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg application/octet-stream;
}

https://qiita.com/RyoMa_0923/items/55078f6fb57e9d70a37f

nginxのブラウザキャッシュ対応

cache-controllを設定し、ネットワーク通信によってサーバから取得されたファイルがブラウザにキャッシュされるようにした。
maxageに関しては今回は基本的に静的なファイルをやり取りすることが多かったので、気にしなかったが、
動的ファイルを使用する場合は、もっと低く設定する必要がある。

nginx.conf

location ~* \.(gif|jpe?g|png|webp|ico|svg|css|js)$ {
  add_header Cache-Control "s-maxage=86400";
}

https://manual.sakura.ad.jp/cloud/webaccel/manual/settings-webserver.html#nginx

ebi_yuebi_yu

Viteの対応

SplitVendorChunkPluginの導入

Vite 2.8 まではデフォルトのチャンク戦略は index と vendor にチャンクを分割していました。これは SPA にはよい戦略の場合もありますが、すべての Vite ターゲットのユースケースに対して一般的な解決策を提供するのは困難です。Vite 2.9 からは、manualChunks はデフォルトでは変更されなくなりました。設定ファイルに splitVendorChunkPlugin を追加すれば、vendor を分割するチャンク戦略を引き続き使用できます:

https://ja.vitejs.dev/guide/build.html

チャンク戦略とはJSのバンドルファイルを複数の小さなファイルに分割することで、サイトのパフォーマンスを改善しようとする戦略。
ブラウザが必要なコードだけをダウンロードすることが、できるので通信量が減る。

viteではsplitVendorChunkPluginを導入することで、チャンク戦略が利用できる

// vite.config.js
import { splitVendorChunkPlugin } from 'vite'
export default defineConfig({
  plugins: [splitVendorChunkPlugin()],
})

https://zenn.dev/toaru_fe/articles/5128fb6a6c21d2

動的importの導入

動的importを導入することで、viteがチャンクをもっと細かく分離してくれるらしい。
実際に導入してみたが、あまりパフォーマンス改善は見られなかった。

// 静的ロードでは、アプリケーションを起動する際にモジュールが読み込まれる。
// モジュールは実際にそのコード部分が呼ばれる前に事前に読み込まれる。
import 'sample' from 'Sample'
const sampleInstance = new Sample()

// 動的ロードでは、アプリケーションを起動する際ではなく
// アプリケーションを起動して実際にそのコードが呼ばれた際に、モジュールが読み込まれる
import('Sample')
  .then(sample => {
      // 動的に読み込まれたsampleクラス
      const sampleInstance= new sample();
    });

パフォーマンスに関わる設定項目の確認

build.minify

ミニファイを無効にするには false を設定するか、使用するミニファイツールを指定します。デフォルトは esbuild で、これは terser に比べて 20~40 倍速く、圧縮率は 1~2%だけ低下します。ベンチマーク

https://ja.vitejs.dev/config/build-options.html#build-minify

build.cssMinify

このオプションによって、デフォルトの build.minify を使うのではなく、CSS ミニファイを具体的に上書きすることで、JS と CSS のミニファイを別々に設定できるようになります。Vite はデフォルトでは esbuild を使用して CSS をミニファイしています。

https://ja.vitejs.dev/config/build-options.html#build-cssminify

build.cssCodeSplit

CSS コード分割を有効/無効にします。有効にすると、非同期 JS チャンクでインポートされた CSS はチャンクとして保存され、チャンクがフェッチされるときに一緒にフェッチされます。

https://ja.vitejs.dev/config/build-options.html#build-csscodesplit

ebi_yuebi_yu

メモリリークの検査

memLabを使ってメモリリークを検出したページについて、
Chrome Dev Toolでメモリリークを詳しく調べた。

https://facebook.github.io/memlab/docs/installation
https://zenn.dev/sa2knight/articles/analyze_javascript_heap_with_memlab

Chromeのdev toolでメモリ使用量のsnpp shotを取り、それをもとにメモリリークが起きている箇所を調べた。
Source Mapをviteで生成し、それをdev toolで読み込むことで原因箇所を特定する

https://scrapbox.io/shimizukawa/Chrome_DevTool_でメモリリーク調査

ebi_yuebi_yu

CSSの非同期ロード

画面描画時にすぐに必要ないCSSを非同期的にロードすることで
CSSによるレンダリングブロックを避ける

<link rel="stylesheet" href="https://test/sample.css" media="print" onload="this.media='all'">
ebi_yuebi_yu

Nuxt3

コンポーネントの動的ロード

Lazyをつけることで必要になったときのみcomponentをロードできるらしい。
今回はページの読み込み時にすべてのcomponentを読み込みたかったので実施しなかった。

<script setup>
const show = ref(false)
</script>

<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" />
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>

https://nuxt.com/docs/guide/directory-structure/components#dynamic-imports

hydrationの遅延

ハイドレーションとは
Webページを作る際、サーバー側でHTMLを生成してブラウザに送るが、このHTMLは静的である。
だが、ユーザーがページで何か操作をしたり、動的な内容(変わる内容)が必要な場合は、JavaScriptが必要になる。このJavaScriptがページに機能を追加して「生きた」ページにする過程を「ハイドレーション」と与呼ぶ。

「nuxt-delay-hydration」はこのハイドレーションを遅らせる機能を提供する。通常、ページを読み込むときには、HTMLがブラウザに届いてすぐにJavaScriptが動き始めて、ハイドレーションが行われる。でも、これを遅らせると、ページがもっと速く表示されることがある。特に、JavaScriptがたくさんあるページでは、この違いが大きい。

「nuxt-delay-hydration」を使うと、特定の条件が満たされるまでハイドレーションを遅らせることができる。例えば、ユーザーがスクロールしたり、特定の時間が経過した後にハイドレーションを開始するように設定できる。

https://github.com/harlan-zw/nuxt-delay-hydration

使用する場合はpluginに設定する

server/plugins/compression.ts

import { useCompression } from 'h3-compression'

export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('render:response', async (response, { event }) => {
    if (!response.headers?.['content-type'].startsWith('text/html'))
      return

    await useCompression(event, response)
  })
})

参考

https://www.vuemastery.com/blog/nuxt-3-performance-pt-1/

このスクラップは2023/12/31にクローズされました