1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

政府広報オンラインのフロントエンド高速化を勝手に提案する

Posted at

以前、中央省庁のWebサイト37サイトのスピードを比較したとき、政府広報オンラインが最下位でした。

貼り付けた画像_2023_11_08_5_25.png
CrUX 霞ヶ関サイトスピード番付

ということで、政府広報オンライントップページのフロントエンド高速化を勝手に提案します。

概要

Lighthouseのスコアで56から91に引き上げる提案です。

LighthouseはPagespeed Insightsの採点エンジンで、同じ内容で計測します。PageSpeed Insightsにも近い効果が見込めます。

score-before-after1.png

動画で見ても違いは一目瞭然です。

← Before | After →

スコア改善のためにやったこと

  • ロングタスクを解消する
  • JavaScript Lazyloadをやめる
  • document.writeによるコンテンツ挿入をやめる
  • パブリックCDNを使わない
  • bxSliderを最適化する
  • 無駄に大きなバナー画像を軽量化する
  • テキストリソースを通信中GZIP圧縮する
  • 3rdパーティJSの読み込みを工夫する
  • 政府インターネットテレビを工夫する

検証方法

私はこの政府広報オンラインの関係者ではありません。

しかし拙作PageSpeed Questで、任意のページについてフロントエンドの改変とそのLighthouseスコアへの影響を、ある程度の精度で仮説検証できます。

なお、Lighthouseは一定条件での試験に過ぎません。指標やスコアは実行タイミングで変動することがありますが、今回は簡易的に計測も1回ずつしか行っていません。読み物としてご了承ください。

変更内容

どのリソースをどのように変えたか、Gitで差分を公開します。詳しく知りたい方はこちらもご覧ください。

仮説検証と提案

では本題に入ります。まず改善前のLighthouseスコアと各指標の値がこちら。

snapshot-score.png

Total Blocking TimeSpeed Indexが悪い(赤)水準にあり、他の指標も要改善(オレンジ)となっています。

3rdパーティタグを一時退避する

まず3rdパーティタグは、アクセス解析や広告など主にクライアント都合で設置されるものであり、制作サイドでは普通は変更できません。

フロントエンドにおいて3rdパーティタグの影響は大きく、Webサイト自体はそこまで重くないのに、3rdパーティタグが理由でスコアが低下しているというケースはけっこうあります。

ここでは制作サイドで作用できる範囲にフォーカスするため、3rdパーティタグをいったん削除します。後ほど復元して対策を検討します。

stash-3rdjs.png

変更点

このページも3rdパーティタグがそこそこ重く、それを外した結果Total Blocking Time990ms660msと一気に1/3も改善しました。

政府インターネットテレビを一時退避する

このページでとにかく重いのが政府インターネットテレビのインライン動画プレーヤーです。

Cursor_と_政府広報オンライン あしたの暮らしをわかりやすく.png

動画データではなくプレーヤーのJavaScriptが重いです。ほんとにクソ重い。

こちらもおそらく外部提供の3rdパーティJavaScriptのようなもので、あまり変更はできなさそうです。いったん削除して他の点にフォーカスし、後ほど対策を検討します。

stash-nettv.png

変更点

Total Blocking Time660ms50msとなり、ブロッキングの問題はほぼ解消されました。

Speed Index18.79.4と大幅に改善されました。

ロングタスクを解消する

この時点で開発者ツールのPerformanceタブを見ると、DOMContentLoadedを起点に長いタスクがメインスレッドを占有しています。

dcl-longtask.png

このようにメインスレッドを50ms以上占有するタスクをロングタスクと言います。

  • ロングタスクの間はユーザーの操作に即座に反応せず、フリーズしたような状態になる
  • Total Blocking Timeはロングタスク基準超過(50msを超えた)分の合計で計算される

ロングタスクは一匹残らず駆逐する決意で臨みます。

jQueryハンドラ乱用問題

上記のロングタスクは、ページ初期化処理の詰め込み過ぎが原因です。これもあるあるです。

jQueryでは以下のように手軽にページ初期化時の処理を書くことができます。

これには罠があり、記述箇所はバラバラでも実行時はそれらがまとめてぶっ続けで実行されるので、無頓着に使うと意図せずロングタスク化します。今の状況はまさにそれです。

$(function() {
  // 初期化処理1 30ms 非ロングタスク
});

$(function() {
  // 初期化処理2 40ms 非ロングタスク
});

// 初期化処理1と2は続けて実行される
// 30ms + 40ms = 70ms > 50ms ロングタスク

一番手軽なのは、こまめにsetTimeoutで括ることです。イベントループに一旦スレッドを返して別タスクに分離でき、ロングタスクへの肥大化を回避できます。

$(function() {
  setTimeout(function() {
    // 重い初期化処理 → 別タスクに分離
  });
});

この考えに基づき、比較的占有時間の長い処理を逃してあげた結果がこちらです。

break-longtask.png

変更点

狙い通りロングタスクを駆逐し、Total Blocking Time0msにできました。

ファーストビューの描画タイミングに鎮座していたロングタスクを散らせたのでSpeed Indexも改善されました。

JavaScript Lazyloadをやめる

昔流行ったJavaScript Lazyloadですが、今はブラウザがネイティブで同様の機能をサポートしています。

JavaScriptで頑張るのは逆に計算量の無駄遣いなので即座にやめましょう。

no-lazyload.png

変更点

指標上は変化はありませんが、JavaScript Lazyloadも先ほどのDOMContentLoadedのロングタスクに関与していました。そのリスクは解消できました。

document.writeによるコンテンツ挿入をやめる

おそらくCMSやサーバーの都合によるものと推測しますが、JavaScriptを用いてコンテンツを動的に挿入(インクルード)する処理が随所で使われています。

その実装にいにしえの秘術 document.write が用いられています。

document.writeはスクリプトをそこから動かせず、非同期にもできません。呪いのようなレンダリングブロック要因になるので、使わないのが吉です。

ここではDOM操作によるインクルード処理に変換し、script要素もできるだけasyncにします。

no-document-write.png

変更点

実はこれでFirst Content Paintが改善できるかな、と期待しましたが指標に変化はありませんでした。やはりやってみないとわからないものです。

パブリックCDNを使わない

jQueryをパブリックCDNから取得しています。

個人的にこの手法は望ましくないと考えてて、CSSと早いタイミングで欲しいJavaScriptはできるだけHTMLと同じサーバーからの配信をお勧めしてます。

  • パブリックCDNは経験上、日本では遅い
  • 別ドメインからの配信にはDNSルックアップやSSLハンドシェイクが生じる

以上がその理由です。特にjQuery本体は依存元が多く、早めに欲しいファイルです。

トラフィックの分散という意図があるかもしれませんが、画像のトラフィックに比べるとCSSやJavaScriptは相当小さく、割に合いません。

fast-jquery.png

これもFirst Contentful Paintを…と期待したものの、指標の変化はなしでした。

bxSliderを最適化する

次にメインビジュアルで用いられているカルーセルスライダー bxSlider の最適化を図ります。

個人的にはファーストビューのカルーセルスライダーは滅びろと思っていますし、このページならCSSベースのもっと軽量なライブラリを使う方がよいと思いますが、bxSliderで頑張ってみます。

今回、問題を感じたのは次の2点です。

  1. スライダーが起動するまでシーンが縦並びに表示され、起動によりレイアウトが変化する
  2. bxSliderがDOMの再構築をするので画像読み込み中に起動すると表示がチラつく

ここまでCumurative Layout Shiftの評価がイマイチなのは主に1.のせいです。

調整したのは、以下です。

  1. スライダーの起動前も1シーン目のみが表示されるようにCSSを調整
  2. 1シーン目の画像が読み込み終わるまでスライダーの起動を待機するようJavaScriptを調整

slider-layout.png

その結果、Cumulative Layout Shiftは狙い通り改善されました。チラつきもなくなりましたが、Largest Contentful PaintSpeed Indexが大幅に悪化してしまいました!

無駄に大きなバナー画像を軽量化する

bxSliderの最適化で逆に指標が悪化したのは、こちらの1枚目の画像ファイルが無駄に大きすぎるためです。

画像はわざわざWebPも用意して頑張っているようですが、PCでも高々 800 x 450 の表示領域に対し、画像自体が 4481 x 2521 もあります。ファイルサイズはなんと1枚で 2.3 MBもあります。

Cursor_と_政府広報オンライン あしたの暮らしをわかりやすく.png

これは単純にアップロードする画像の間違いだと思われます。

最近はWebの専門ではない人がサイトを更新することが多いですが、CMS等で解像度の正規化を入れた方がいいです。ほんと素人さんだとこのようにびっくりする画像を上げたりするので…

ここでは、この2.3 MBの異常に大きな画像bn_u_20221001_sl.webp44 KBに軽量化しました。

# いったんPNGにする
dwebp -o bn_u_20221001_sl.webp.png bn_u_20221001_sl.webp
# 適切な解像度にリサイズする
convert bn_u_20221001_sl.webp.png -resize 800x450 bn_u_20221001_sl.webp.resize.png
# pngquantで減色する
pngquant --output bn_u_20221001_sl.webp.resize.quant.png 32 bn_u_20221001_sl.webp.resize.png
# 再度WebPにする
cwebp -lossless -o bn_u_20221001_sl.min.webp bn_u_20221001_sl.webp.resize.quant.png

optimize-banner.png

変更点

これでLargest Contentful PaintSpeed Indexを正常化できました。

テキストリソースを通信中GZIP圧縮する

このページではテキストリソース(HTML、CSS、JavaScript、SVG)が、配信中に圧縮されていません。

圧縮と解凍による負荷を避けるという理由なのか、単に設定忘れなのかはわかりませんが、圧縮はやった方がいいです。20年前ならいざしらず、インターネットにデータを流すことに比べたらデータの圧縮と解凍なんて今や空気みたいなものです。

gzip.png

変更点

実際、テキストリソースを通信中GZIP圧縮することでFirst Contentful PaintLargest Contentful Paintが改善されました。

3rdパーティタグと政府インターネットテレビ

3rdパーティタグと政府インターネットテレビの問題はいったん棚上げしていますが、ここまででスコア95とかなり改善できました。

gzip-with-score.png

気になる点はありますが、ひとまずスコア改善はここまでにします。

退避した3rdパーティタグを工夫する

3rdパーティタグをonloadで挿入する

ページ本来のコンテンツと3rdパーティタグはメインスレッドに同居し、時間を常に食い合う関係にあります。

コンテンツの表示中に3rdパーティJavaScriptが動き出すと、その分本来の機能は遅延します。

ここでは退避していた3rdパーティタグを復元しますが、script要素を挿入するタイミングをwindow.onloadに遅延させてみました。

delay-3rdjs-onload.png

変更点

せっかく0msにできたTotal Blocking Timeは悪化しますが、本来のコンテンツ表示にCPUを優先的に譲れるので、少しマシになるはずです。

3rdパーティタグは常に断捨離を

このページでは比較的3rdパーティタグは少ないですが、一般企業のサイトだと、Google Tag Managerが責任者不明のタグのジャングルになっているケースも珍しくありません。

空気のようなものと思って無頓着に追加するうちに主従が完全に逆転し、本来のコンテンツ表示より3rdパーティのJavaScriptが何倍もCPUを消費していることもあります。

政府広報オンラインでも、トップページについて言えば以下のFacebook SDKは必要そうに見えません。

	<script>(function (d, s, id) {
			var js, fjs = d.getElementsByTagName(s)[0];
			if (d.getElementById(id)) return;
			js = d.createElement(s); js.id = id;
			js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v2.9";
			fjs.parentNode.insertBefore(js, fjs);
		}(document, 'script', 'facebook-jssdk'));</script>

部屋の片付けと一緒で、ひとつずつ捨てる決断をするのは大変です。なので3rdパーティタグは一度全部削除してしまって、本当に困ったタグから改めて追加するようなことをしてもよいと思います。

政府インターネットテレビを工夫する

最後に、最大の問題である政府インターネットテレビをどうにかしたいと思います。

ぶっちゃけ再生する人はかなり少ないと思われます。それなのに全員にインライン再生用のクソ重いJavaScriptを問答無用に読み込ませて使用感を悪化させているのはあまりに非効率です。

いくつかアイデアがあります。

  1. video要素を使う
  2. クリックしてからプレーヤーを読み込み自動再生する
  3. ページの下部に配置しスクロールに応じてプレーヤーを読み込む
  4. iframeでメインスレッドから逃す

video要素を使う

大人の事情でダメそうですが、おそらく一番シンプルです。

クリックしてからプレーヤーを読み込み自動再生する

プレーヤーをあらかじめ読み込んで再生に備えるのではなく、クリックされたからプレーヤーを読み込み、自動再生するという案です。クリックから再生まで時間はかかりますが、再生率が低いのであれば投機的にこの方が正解でしょう。

この手法であれば初期状態で必要なのは動画のポスター画像だけなので負荷はほぼありません。

optimized.png

変更点

ページの下部に配置しスクロールに応じてプレーヤーを読み込む

政府インターネットテレビをファーストビューの一等地に置くのではなく、ページ下部に配置するという案です。

これもおそらく趣旨に反してNGと思われますが、ページ下部にあればプレーヤーの起動はページ読み込みと同時ではなく、ある程度スクロールしてからに遅延できます。ファーストビューの体験を改善できます。

iframeでメインスレッドから逃す

インライン動画に見えて実はiframeになっているという案です。

最初はこの案がよいと思ったんですが、Chromeではiframeのオリジンが同じTLDだとメインスレッドを共有するようで、効果なしでした。

別のTLDではiframeは別スレッドになったものの、Lighthouse上ではTotal Blocking Timeが合算されてしまい、スコア上は目立った成果が出せませんでした。実質としてはこれでよいという気はしますが。

結論

3rdパーティタグはonloadのタイミングに遅延させ、政府インターネットテレビはサムネイルだけを表示し、クリックでプレーヤーを読み込むようにする、というのが落とし所かと考えます。

それによるスコア改善結果がこちらです。

score-before-after1.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?