ようこそPageSpeed Insights沼へ。

JAMStackのいいところは静的コンテンツゆえにページの読み込みが高速なことなのですが、 このブログのLighthouseのPerformanceスコアは60から70で微妙だったので対応しました。
このブログはGridsome v0.7.23 (Vue 2)で実装しているため、Vue 2向けの対策記事になります。

直近の記事を使用した測定結果はこうなりました。個人的には満足です。

Lighthouse Mobile
Lighthouse Mobile
PageSpeed Insights Mobile
PageSpeed Insights Mobile

効果が高かった対策

以下は効果が高かった必須の項目です。これで20くらいスコアが上がりました。

Google Adsenseを遅延ロードする

PageSpeed InsightsやLighthouseと同じGoogleのサービスにも関わらず、Google Adsenseは高速化に対応しておらず、死ぬほど足を引っ張るため遅延ロードさせました。
なお、このサイトでは自動広告をオフにしているため、オンにしている場合に正しく広告が読み込めるかは保証できません。

スクロールしたタイミングで https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js を読み込みます。 ついでなので、2021/07/19にリリースされた 新しい AdSense コードで広告のパフォーマンスを改善する - Google AdSense ヘルプ の対応も一緒に行いました。

export default {
  data() {
    return {
      lazyloadads: false,
    };
  },
  mounted() {
    this.lazyloadads = false;
    window.addEventListener("scroll", this.onScrollLoadAds);
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.onScrollLoadAds);
  },
  methods: {
    onScrollLoadAds() {
      if (
        !this.lazyloadads &&
        (document.documentElement.scrollTop != 0 ||
          document.body.scrollTop != 0)
      ) {
        const url =
          "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client={YOUR_ID}";
        if (!document.querySelector(`script[src="${url}"]`)) {
          let ad = document.createElement("script");
          ad.async = true;
          ad.setAttribute("crossorigin", "anonymous");
          ad.src = url;
          let sc = document.getElementsByTagName("script")[0];
          sc.parentNode.insertBefore(ad, sc);
        }
        this.lazyloadads = true;
      }
    },
  },
};

Adsenseのadsbygoogle.push()に関しては、ロード後にDOMを生成しないと以下のエラーが発生します。

"adsbygoogle.push() error: All ins elements in the DOM with class=adsbygoogle already have ads in them."

今回はAdsenseのinsタグにv-if="lazyloadads"を設定して回避しました。広告が表示されるまでにラグが発生するので収益に影響が出そうですが、特に気にしてないのでこのまま行こうと思います。

また、すでにscriptタグが追加済みの場合に再追加してしまうと以下のエラーが発生します。

adsbygoogle.push() error: Only one AdSense head tag supported per page. The second tag is ignored.

SPAの場合ページ遷移時にscriptはロード済みのため、今回はdocument.querySelectorで追加済みか判定して、判定済みの場合は何もしないことで回避しています。

Google Fontsを自分のサーバーにホスティングする

以下のサイトの解説が大変丁寧なのでここで解説することは特にないです。ありがたいです。
Self-HostなGoogle Fontsを使ってPage Speedに怒られないようにする | tm23forest.com

google webfonts helper で使っているフォントをセルフホスティングに対応させます。対応前にフォントのライセンスは確認しておいたほうがよいでしょう。

静的コンテンツをクライアントキャッシュの対象にする

静的コンテンツにExpiresヘッダを付与しました。nginxの設定は次のようにしています。

location ~* \.(js|css|jpg|jpeg|gif|png|swf|ico|pdf|svg|eot|ttf|woff|woff2)$ {
    expires 30d;
}

効果がよくわからなかった対策

以下はやってはみたものの効果がわからなかった対策です。

reCAPTCHA v3の利用をやめる

reCAPTCHA v3の性質上、全ページで読み込まないと効果が出ないため全ページで読み込んでいましたが、reCAPTCHA v3ごと利用をやめました。
今まで1週間に1回くるかこないか程度だったスパムメールの着信が、1時間に2回くらいに増えたのでおすすめしません。耐えられないレベルではないのでこのまま運用していますが、もとに戻すかは悩みどころです。
reCAPTCHA v3は遅延ロードすると効果がなくなりそうな気がするのですが、測定してないのでどうなるか不明です。

iOS向けの:hoverや:activeを有効化するための修正を改善

iOSだと:hover:activeが効かなくなるため、擬似クラスを適用させたい要素にontouchstart=""を指定していたのですが、 「スクロール パフォーマンスを高める受動的なリスナーが使用されていません」という警告が表示されるため、 ontouchstart属性の使用をやめて、次のメソッドを使うように修正しました。

export default {
  mounted() {
    window.addEventListener("scroll", this.onTouchStart);
  },
  beforeDestroy() {
    window.removeEventListener("scroll", this.onTouchStart);
  },
  methods: {
    onTouchStart() {
      if ("ontouchstart" in document.documentElement) {
        document.addEventListener("touchstart", () => {}, { passive: true });
      }
    },
  },
};

警告は消えたのですが、スコアに影響があったのかはわからないです。

所感

PageSpeed Insightsは沼みがありますね。Qualys SSL LabsのSSL Server Testとか、こういうタイプのスコアを上げるのが好きなので楽しかったです。Adsenseの対策が難しかったので、デフォルトで遅延ロードとSPA向けの対応がされればよいなぁと思いました。