パフォーマンスチューニングを勉強したのでまとめた (LightHouse編)

はじめに

Cyber AgentのWeb Speed Hackathon Vol.2に参加するのでフロントのパフォーマンスチューニングを少し勉強しました。(執筆時は未参加)

今回はLight House v6の指標についてまとめました。
web.devの内容をなぞっていきます。

パフォーマンス測定の指標

LightHouse 6は以下の6つの指標に基づいて測定される。()の数値は重視される比率。


OGP imageLighthouse performance scoring  |  Chrome for DevelopersLearn how Lighthouse generates the overall Performance score for your page.

1. First Contentful Paint (FCP)

weight: 15%

FCP measures how long it takes the browser to render the first piece of DOM content after a user navigates to your page.
ユーザーがページに訪れた際に最初のDOMがレンダリングされるまでにかかる時間。

考えられる原因

→ 対応:

  1. font-display: swapを追加
  2. Webフォントをpreloadする
 <link rel="preload" href="/assets/Pacifico-Bold.woff2" as="font" type="font/woff2" crossorigin>

2. Speed Index

weight:15%

Speed Index measures how quickly content is visually displayed during page load. どれだけ速く、ページが視覚的に表示されるか。

改善策
  • Minimize main thread work
    レンダリングプロセスの根幹を司るmain threadを最適化すべし。
    main threadはHTMLのparse, DOMの構築, CSSのparseと適用, そしてJSのparse, 評価, 実行と言った、コードの大部分を扱う。
    またmain threadはユーザーからの操作(イベント)に対応する為、常に忙しいが、これを後回しにするとUXへ影響する。 対応: (多すぎて書ききれない...)
  1. web workerを使用して、JavaScriptをmain threadで使用しない。(link: Use web workers to run JavaScript off the browser's main thread)
  2. CSSで複雑なセレクタや数値計算を避ける。

.box:nth-last-child(-n+1) .title { /* styles */ }
  1. CSSのminify, 使っていないコードの削除
  2. etc...

 MinificationとData Compressionをしよう。

Minification
CSS,JSなどのコードファイルのminifyをしろ。(webpack v4以降なら自動でやってくれるからいらないよ👍)

Data Compression
サーバー - クライアントのデータのやりとりを最適化しよう。GzipもしくはBrotliで。BrotliはGzipよりいいよ。
ブラウザへ送るファイルを圧縮するには DynamicStatic の2通りのやり方があるよ。どちらも一長一短だよ。

Dynamic comporession
リクエストが来た時にファイルを最適化するよ。手動で最適化したり、ビルド中にするより簡単だよ。けど圧縮率(?)が上がると遅延が生じるよ。
expressではcompression middlewareライブラリを使えば静的ファイルをgzipでdynamic compressionできるよ。

const express = require('express');
const compression = require('compression');

const app = express();

app.use(compression());

app.use(express.static('public'));

const listener = app.listen(process.env.PORT, function() {
	console.log('Your app is listening on port ' + listener.address().port);
});

Brotliにはshrink-rayが使えるよ〜

Static comporession
事前に最適化して保存しておくよ。ビルドが長くなるけどアクセス時の遅延は起こらなくなるよ。
webpackのpluginが使えるよ。


module.exports = {
	plugins: [
		new CompressionPlugin()
	]
}

3. Largest Contentful Paint (LCP)

weight: 25%

The Largest Contentful Paint (LCP) metric reports the render time of the largest image or text block visible within the viewport.
viewport内で一番重い画像やテキストのレンダリング時間。

  • <img>
  • <svg>内での<image>
  • <video>
  • cssのbackground imageなど、url()を使うもの

対応

4. Time to Interactive (TTI)

weight: 15%

Measuring TTI is important because some sites optimize content visibility at the expense of interactivity. This can create a frustrating user experience: the site appears to be ready, but when the user tries to interact with it, nothing happens.

一定数のサイトはinteractivityを軽視してコンテンツの表示速度ばかり重視している。

指標

A page is considered fully interactive when:

  • The page displays useful content, which is measured by the First Contentful Paint,
  • Event handlers are registered for most visible page elements, and
  • The page responds to user interactions within 50 milliseconds.

FCPによって測定された有意なコンテンツが表示され、イベントハンドラがほとんどの要素に登録され、ユーザーの動作に50ms以内でに反応する事。

対応

5. Total Blocking Time (TBT)

weight: 25%

TBT measures the total amount of time that a page is blocked from responding to user input, such as mouse clicks, screen taps, or keyboard presses.
ユーザーの入力への返答がブロックされた時間(blocking portion)の合計。
50ms以上かかる処理はlong taskとされる。
例えば70msの処理は70 - 50で超過分の20msがblock portionとされる。
参考: Are long JavaScript tasks delaying your Time to Interactive?

対策:

6. Cumulative Layout Shift (CLS)

weight: 5%

cumulative(意:累計)

レイアウトが頻繁に変わりすぎるページだとUXを損なうからよくないよ〜

対策

  • width,heightの動的な変更ではなく、 transform: scale()を使おう
  • top, right, bottom, leftを動的に変更せず、transform: translate()を使おう

気付き

  • 指標は6つあるが、改善策はそれぞれに固有の物ばかりでなく共通の物が多い。
    Minimize main thread work(JS)
    Reduce JavaScript execution time

  • ブラウザ(LightHouse)でJSファイルを見てもchunkされた状態になっているので、webpackのビルドプロセスを把握する必要がありそう。
    参考: https://qiita.com/mizchi/items/418be9abee5f785696f0

次回はwebpackの最適化とかをまとめていくつもりです。