⚡
Vuetifyを使ったSPAで最低限のPageSpeedInsightsスコアを獲得するまでに取り組んだこと
この記事は何
- ReactでもVueでも、何かしらのUIフレームワークを活用することが多いかと思います。私が個人開発したサイトComicLakeでは、Vue(Nuxt)+Vuetifyを活用していますが、何の工夫も打たないとページ表示速度の観点でPageSpeedInsightsさんからボロクソに言われます笑🤣
- ページ表示速度改善のためのTipsはいろいろなところで見かけますが、情報が古かったり散らばっていたりするので、現時点の情報をまとめておくことにも価値があると思い、私の開発体験を通じて得たTipsをまとめておきます。
本記事で取り扱う内容
- 外部リソース(アイコン、フォント)の読み込み最適化
- 不要なVuetifyリソースの除外
- 描画コストがデカすぎるコンポーネントの扱い
前提条件
各モジュールの導入方法などは個別に調べて下さい
- nuxt 2.14.12
- vuetify 2.4.6
- nuxt-webfontloader 1.1.0
- @mdi/js 5.9.55
- nuxt-purgecss 1.0.0
ピュアなVue.jsでも大筋は変わらないと思いますが、どこに設定を書くかとかが変わってきます。
本編
徒然なるままに🖋
外部リソース(アイコン、フォント)の読み込み最適化
必ず怒られるのが外部リソースの読み込みです。処置するファイルごとに分けて、設定方法を記述していきます。
nuxt.config.js
nuxt.config.js
export default{
buildModules: [
'@nuxtjs/vuetify',
],
modules: [
`nuxt-webfontloader`,
],
vuetify: {
defaultAssets: false,
optionsPath: './vuetify.options.js',
customVariables: ['~/assets/variables.scss'],
},
// just example below
webfontloader: {
custom: {
families: ['Roboto:n3,n4,n5,n7', 'Saira Extra Condensed:n7'],
urls: [
'https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap',
'https://fonts.googleapis.com/css2?family=Saira+Extra+Condensed:wght@700&display=swap&text=ComicLake',
],
},
},
}
- デフォルトではアイコン、フォントともにCDNを使って外部から読み込みます。その方法ではリソースの読み込み速度が不利なので
defaultAssets:false
として無効化します。 - その場合アイコンのオプションを指定する必要があるため、
optionsPath:ファイル名
で指定します。 - フォントについては「前提」に記載した
nuxt-webfontloader
を使って非同期読み込みを行います。その設定もnuxt.config.js
の中に記載します。- ポイントは①使用するフォントの中でも使用する書体のみに絞ってインポートを行うこと、②使う文字が限られている場合(例えばロゴ等)は
text=***
の形式で読み込む文字を限定することの2点です。 - デフォルトのフォントファミリーを変えたい場合などは、
customVariables
に記載した別ファイル内で設定を行います。
- ポイントは①使用するフォントの中でも使用する書体のみに絞ってインポートを行うこと、②使う文字が限られている場合(例えばロゴ等)は
vuetify.options.js
vuetify.options.js
export default {
// for production (not using mdi cdn)
icons: {
iconfont: 'mdiSvg',
},
}
- Vuetify公式にも記載されているように、使用するアイコンだけを限定してimportするのが好ましいです。そのための前準備として上記設定を行います。
- 各.vueファイル内での使い方は上記公式ページを参照下さい。
- 使用可能なアイコン一覧は、個人的には下記リポジトリが一番調べやすかったです。
./assets/variables.scss
日本語フォントなど、ローカルフォントを活用する際に、デフォルト設定を上書きするには下記のように記述する。
variables.scss
$body-font-family: 'Roboto', 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', メイリオ, 'MS Pゴシック', Meiryo, Arial, Tahoma, sans-serif;
不要なVuetifyリソースの除外
何も処置せずとも、productionモードでは使用しているコンポーネントだけがbundleされるはずなのですが、CSSについては全てがbundleされてしまいます。次の方法で対処します。[1]
nuxt.config.js
nuxt.config.js
export default{
buildModules: [
'nuxt-purgecss',
],
purgeCSS: {
enabled: process.env.NODE_ENV === 'production',
paths: [
'components/**/*.vue',
'layouts/**/*.vue',
'pages/**/*.vue',
'plugins/**/*.js',
'./node_modules/vuetify/dist/vuetify.js',
'assets/**/*.scss',
],
styleExtensions: ['.css'],
whitelist: ['v-application', 'v-application--wrap', 'layout', 'row', 'col'],
whitelistPatterns: [
/^v-((?!application).)*$/,
/^theme--*/,
/.*-transition/,
/^justify-*/,
/^p*-[0-9]/,
/^m*-[0-9]/,
/^text--*/,
/--text$/,
/^row-*/,
/^col-*/,
],
whitelistPatternsChildren: [/^v-((?!application).)*$/, /^theme--*/],
extractors: [
{
extractor: (content) => content.match(/[A-z0-9-:\\/]+/g) || [],
extensions: ['html', 'vue', 'js'],
},
],
},
}
描画コストがデカすぎるコンポーネントの扱い
- 私が開発したWebサイトは下記キャプチャ画像のように、画像やボタン・タイトルを含んだリストを大量に並べます。
- 大量にコンポーネントを描画することは、描画コストが高いです。対処方法としては①virtual-scrollを使って画面に表示される範囲のみ描画する、②各コンポーネントの描画コストを下げるの二択があるかと思います。
virtual-scrollの使用
vuetifyにもv-virtual-scroll
コンポーネントがあるため、そちらを使ってあげると良いかと思います。
各コンポーネントの描画コスト削減
何らかの理由(例えば私の場合、2-columnがv-virtual-scrollではうまく実装できなかった)でvirtual-scrollを使わない場合はこちらの手段を選択する必要があります。
- 試しに
v-btn
コンポーネントを含むリストを100個ほど並べてスクロール頂くと分かるのですが、画面描画が追いつきません(良好な状態を60fpsとすると、30fpsとかまで落ちるケースがありました)。 - Vuetifyの全てのコンポーネントがそうなるわけではありません。内部的に
functional component
を使っているかどうかが運命の分かれ道だと推察しています。 - Vue開発者ツールで調べてみると分かりますが、
functional
ではないコンポーネントが大量に描画されると、描画速度がガタ落ちしてきます…。 - この場合、極論ですがvuetifyを使わずに実装する必要があります。私の場合、下記のようなカードコンポーネントを自前でゴリゴリ実装しました…。ただ自前で実装すれば良いわけではなく、忘れずに
functional component
で実装していきます。- 自前といっても、Vuetifyのcssヘルパーは使っても大丈夫ですので、うまいこと活用すると良いかと思います。
- 日本語で読める
functional component
についての記事はこちらを参照。
おわりに
記事の途中にも書きましたが、Vue.js v2系だからこそ苦労している点もあるので、v3系に上げたらどうなるかも、追い追い検証してみたいと思います。
キャプチャ画像を見てサイトに興味を持っていただけましたら、下記にもアクセスをお願いします 🚀
-
こちらを参考にさせて頂いています。https://qiita.com/nogutk/items/58370cd8a713111be9bc ↩︎
Discussion