みるめも

プラグインなしで既存記事全てのアイキャッチだけWebp化してページ高速化する(Cocoonの例)

プラグインなしで既存記事全てのアイキャッチだけWebp化してページ高速化する(Cocoonの例)
みるみ
Follow Me!
みるみ

雑記ブロガー。文章を書くのが好き。たまにライターの仕事もしています。

Cocoonを使ったサイトカスタマイズが得意です(このブログもCocoon)

2021年の5月にページエクスペリエンスの考慮が強化されたGoogle検索アルゴリズムアップデートがあると言われています

Core Web Vitalsの導入が進んだり、PageSpeed Insightsのスコア形態やドキュメントもそれに類するものになってきたことを考えるとこの流れは必然です。

というわけで、これまで「コスト(時間/手間)に対するリターン(SEO効果)の低さ」からあんまり腰を据えて取り組んできていなかった高速化ですが、ちょっとずつ自分で実験しながらやってみることに。

これまでは「結果としてユーザーが体感するページ表示速度」が速ければそのプロセスはどうでもいいと考えていました。つまりPageSpeed Insightsのスコアを上げようとする作業はほぼ無駄という考え方です実際、ページの表示速度を上げることとスコアを上げることには直接の関連はありません。これを理解できていない人は本当に多いので注意してください。詳しいことが知りたければ、少し難しいですが この記事 が一番よくまとまっていると思ってます)

ですがページエクスペリエンスアップデートなるものがあると仮定した場合、PageSpeed Insightsで使われている各スコアやWeb Vitalsの指標が単純にアルゴリズムに関わってくる可能性がこれまでよりは強いはずです。

なので「できることはやっておこうかな~」というわけですね。

今回はその手始めとして、トップ3くらいで影響が大きそうなアイキャッチをなんとかしてみます。

やること

今回やることは

各記事のアイキャッチ画像のみをWebpに置き換え、今後の運用でも負担なくWebpで投稿し続けられるようにする

です。

これにあたっての弊害は以下。

  • 何が何でもプラグインは増やしたくない
  • クソくらえのIEが相変わらずWebp非対応
  • WebpってOGPとして全てちゃんと機能するん?
  • これまでアイキャッチ画像を参照していたリンクとか死んだらヤバくないですか

というわけで、これらを全部解決するスタイルを考えて実装します。

なお最大のポイントは今後のブログ投稿時の負担が限りなく小さくあるべきことであり、これまでのアイキャッチの置換が全て成功したとしても通常運用の工数が増えてたらボツです。普段から全てそういう考え方ですが。

 

ちなみになぜ「アイキャッチ画像しかWebp化しないのか」ですが、

  • アイキャッチ以外の記事中で使われている画像は全て遅延読み込みの対象になっているので、FCPを始めとしたパフォーマンスにも実際の表示速度にも影響はない(はず)
    ※遅延読み込みだろうと画像が軽量化されている方が良いに越したことはないけど、LTE回線で自サイトを閲覧してても遅延読み込みの表示が遅いと感じることはないので問題はないと判断
  • 今回の方法だと「全ての元画像(jpg|png|gif) + Webp化した画像」がサーバーに溜まっていくのでちょっと色々問題が多そう
    ※サーバーの容量的には全く問題ないけど、同名ファイルが増えるのは管理の上でもめんどくさそう
  • 単純に作業量の問題
    ※実運用上の工数減が優先とはいえ、この置き換え実装自体にも無限に時間を使えるわけではない

みたいな感じです。

やり方

大まかな手順はこう。

  1. ローカル上に「アイキャッチに使った画像ファイルを実際に使われているファイル名で集めたフォルダ」をつくる
  2. 好きな変換ツールでWebpに一括変換する
  3. FTPでまとめてアップロード
  4. PHP側でデフォルトは.webpで出力するようにテーマファイルを編集
  5. IE対応、srcsetとか邪魔者排除、その他諸々調整作業
  6. 実際の運用

です。

この結果、「記事上では最終的なHTMLが書き換えられたことによってWebp画像が表示されているだけで、元画像は残っているので直リンやOGPなどは何も問題なし、IEでもそれを使える」という状態になります。

以下、この手順を実現するための具体的な手段を紹介します。

ちなみに画像のピクセルサイズは 1200x630 を死守です(後ろでちょこっと触れてます)。

1.ローカル上にアイキャッチ元画像をひとまとめにする

数百もあるアイキャッチ画像のWebp変換をまさか1つずつなんてやってられないので、何かしら一括で行えるよう1つのフォルダにまとめます。

みるみ
みるみ

何の変換ツールを使うか全く決めないで作業したので、結果的にこのあとアホみたいな展開になります(いつも)

サイト上で実際に使われているファイル名のリストを作る必要があったので、DBに目をつけました。

my-mysql-phpmyadmin

幸い、レンタルサーバーにはphpMyAdminがあるので初心者でもDBを触ることができます。

wp_postsには投稿した画像の投稿名(=ファイル名)が記録されているので、適当に抽出してソートします。

僕の場合、

post_titleREGEXP.+
post_parent!=0
post_typeattachment
post_myme_typeREGEXPimage/[jpeg|png]

でSELECTしたらいい感じにアイキャッチ画像だけ抜き出せました。

普段の投稿画像は必ず「title属性なし/alt属性あり」にしているんですけど、アイキャッチには記事タイトルをtitleで入れているので助かりました。

こういうのがない人は、テーマファイルのアイキャッチを出力しているところでダンプしてテキストファイルかなんかにファイル名を書き出す、とかでもいいかも。あれ、っていうかこっちの方が楽だったか…?

そしたらCSVでエクスポートして、post_title(かpost_name)だけ抜き出します。

eye-catch-file-name-list-csv

まあなんか、各自でうまいことやってください(雑)

そしたらこれを使って自分のPCのフォルダ上に実際に集める。

eye-catch-file-name-list-txt

ファイル名だけ抜き出してテキストファイルにして…

select-eye-catch-files-tool

即席で作ったVBアプリケーションでズバババーっとPCから検索して1箇所にコピーさせます。

select-eye-catch-files-tool-result

おっしゃ完成。

2.変換ツールでWebpに一括変換する

例によって事前調査をしないアホなのでここで色々予想外なことが起こりましたが、まあゴリ押しです。詳細は省きます。

最終的に使ったのはサルワカくんのお道具箱。何から何まで尊敬しているサイトですがまたお世話になりましたね。→これ

50個ずつしか変換できないのでまあ地道にやります。記事数500ある人でも10回なのでちょろいもんですね。

ここは特に難しいことはないので次へ行きます。

3.FTPでまとめてアップロード

これも簡単。
出来上がったWebp画像群をまとめてwp-content配下にアップロードするだけ。

ただし、画像の投稿を年月フォルダ整理にしている人は別の作業が必要になります。

最終的に「HTMLソース上のアイキャッチ画像のsrcの拡張子のみをwebpに置換する」ので、もとの画像が置いてある同じディレクトリにアップロードする必要があるからです。

なので対応は二択。

  1. アイキャッチ画像だけは頑張って年月フォルダからwp-content直下に移動する(というか年月フォルダの仕分けは特にメリットないと思うので良い機会だと思ってやめるのもヨシ)
  2. Webp画像のアップロードをもとの年月フォルダに合わせる

です。

これは僕はやっていないので良い方法は思いついていませんが、まあ地道にやる以外ないでしょう。画像だけ動かしても投稿の編集からアイキャッチを適用させないとパスの変更が適用されないので注意してください。

こういうときの頼みの綱であるSearch Regexも、画像には効かないんだよなぁ。

4.PHP側でデフォルトは[.webp]で出力するようにテーマファイルを編集

無事Webp画像のアップロードが終わったら、アイキャッチとして表示させる画像が.webpの方になるようにカスタマイズします。

Cocoonだと、tmpフォルダにあるeye-catch.phpというファイルが該当します。

cocoon-eye-catch-soure

こんな風になっているので、

if (has_post_thumbnail()) {
  $thumbnail_id = get_post_thumbnail_id();
  $eye_img = wp_get_attachment_image_src( $thumbnail_id , 'full' );
  $url = $eye_img[0];
  $width = $eye_img[1];
  $height = $eye_img[2];
  $size = $width.'x'.$height.' size-'.$width.'x'.$height;
  $org_ex = preg_replace("/(.*\.)(jpg|jpeg|png)/mi", "$2", $url);  //足した
  $url = preg_replace("/(.*\.)(jpg|jpeg|png)/mi", "$1webp", $url);  //足した
  $attr = array(
    'src' => $url, //足した
    'class' => "attachment-$size eye-catch-image type-$org_ex",  //足した
);

こんな風に追記します。

ここで

$url = preg_replace("/(.*uploads\/)\d\d\d\d\/\d\d\/(.*)/mi", "$1$3", $url);

のようなコードを書いて年月フォルダ問題を解決する方法も思いつきましたが、IE対応をするときにだいぶ面倒くさいことになるのでやっぱり微妙ですね。できないことはないですが…。

とりあえずこの時点でブラウザの表示を確認してみます。

webp-eye-catch

大丈夫そうですね。

5.IE対応、[srcset]とか邪魔者排除、その他諸々調整作業

細々した作業を片付けます。

一番大きいのはWebpが表示できない産業廃棄物ブラウザことInternet Explorerくんの対応です。

このブログはページキャッシュが効いているのでPHP側でのブラウザ判定→処理分岐は✕です。なので今回はJavaScriptを使います。

みるみ
みるみ

今回の話に限らず「PC/スマホ判定」みたいになにかと条件分岐するときのコードがPHPになっている記事が多いですが、キャッシュ系プラグインを入れている人は絶対やめた方がいいですよ。

というわけでJSのコード。

let imgTag = $('<div>').append($("figure.eye-catch > img").clone(true)).html();
let imgElement = document.getElementsByClassName("eye-catch-image");
let type;
if (imgElement[0].classList.contains("type-jpg")) {
  type = "jpg";
} else if (imgElement[0].classList.contains("type-jpeg")) {
  type = "jpeg";
} else if (imgElement[0].classList.contains("type-png")) {
  type = "png";
} else {
  ;
}
imgTag = imgTag.replace(/(.*\.)webp/gmi, "$1" + type);
$("figure.eye-catch > img").remove();
$("figure.eye-catch").prepend(imgTag);

実はさっきPHP側に足したコードによってもとの画像の拡張子がなんだったかが記録されているので、それに基づいて.webpになっている部分を.jpgとかに直す、というものです。

だいぶ付け焼き刃感のある実装ですけど、IEにはこの程度でお似合いだしそもそもWordPress内なら問題は起きないでしょう。負担も少ないしまあこれでいいかなと。

結果を見てみます。

webp-eye-catch-ie

OK!

僕の環境だと、レンダリングされてからJSで置換している素振りは全く見えません。普通にサーバー側からそのまま配信されている描画に見えますね。この辺はその他の表示速度とかにもよるのかも。

あとは細々した作業なので割愛します。
どうせこんな記事を参考にする人は未来永劫現れないと思うので、、、

6.実際の運用

実際に今後記事を投稿していくときはどういう運用になるかを書いていませんでした。

こう。

記事にアイキャッチ登録するのは[jpg|jpeg|png]の従来通りの画像で、同名ファイルをSquooshでWebp変換して用意→それを単品アップロードする

つまりやることは Squoosh というサイトで1枚Webp変換するだけで、これは本当に一瞬で終わるのでかなり楽な運用かと思います。

1記事投稿につき1枚の変換と1枚のアップロードだけなら今後ずっと継続するのもなかなか費用対効果が良さそうです。

squoosh

SquooshはWebpだけではなくわりと珍しい画像変換もできちゃうナイスなWebアプリ。今まで僕が見たWebアプリで一番イケてるかも、と今思っています。

squoosh-moz-jpeg

なぜか毎回デフォルトが謎の「MozJPEG」というのになっているのだけが「?」です…。

CLIで使えるらしいので「マジか!?自動化バンザイ!!」とか思ってたんですけど、ベータ版っぽくてドキュメントもあまり充実しておらず断念しました。

$ npx @squoosh/cli <options...>

npx対応なのでこれだけでも一応使えるは使える。

それと、パラメータ調整もしました。

  • 操作の少なさ
  • 画質とファイル圧縮率のバランス

を総合的に鑑みて

  • Effort:6
  • Quality:67

としています([Advanced settings]は触らなくていい)。67はいつもみたいに素数で選んだのではなく、本当に67が一番良い値でした。たぶん。

 

手順と運用についての説明はこれでおわりです。

アイキャッチ画像をWebp化することのメリット

というわけで、今回の施策を行うとどれくらい良いことがあるかのお話。

そもそもWebp化している時点で画像サイズが桁違いに小さくなっているので良いことがないはずないんですが、簡単にまとめておく。

ちなみにですが、当然のごとくWebp化せずにあれやこれやして画像サイズを縮小化できないかは相当実験を繰り返しました。

TinyPNG は継続して使っていますが(アイキャッチの元画像にも適用継続)、これをはじめとしてフォトショやResizeアプリを諸々試した結果、無理という結論になりました。

画像のピクセルサイズ自体を小さくする手段もありますが、

  • 画質の劣化が目視で分かる臨界点が意外とすぐ来る(横1000でもうダメだった)
  • OGP(特にFacebook)でも実際にこの値が推奨されている

などもあり却下しました。

記事を開いて一番最初に見えるし、しかも実際に大きいので画像はくっきり綺麗に見てもらいたいです。「限りなく品質は落とさないままページを高速化する」というスタンスでした。

FCP・FID・LCPが改善される

よく考えたらメリットはほとんど1つに集約されますね。

とにかく一番でかい画像が一番上にあるというのがページの表示という観点からは超問題なので、アイキャッチが色んなボトルネックになっているケースは多いと思います。

遅延読み込みも意味ない、かといって位置を下げたらアイをキャッチしない(許せる人はそれでもいいと思うけど、じゃあ貴重なファーストビューに一体何を載せるの?という話にもなる)。

というわけで極力ファイルサイズを小さくするしかないということで、Webpが登場したわけです。

このWebpというヤツは凄まじくて、普通に用意したjpg・pngファイルと比べて平気で10%~20%のサイズになるので、サイト全体で見たときの効果はかなりのものがあるはずです。

PageSpeed Insightsでは「Origin Sumally」といってサイト全体のページ表示パフォーマンスを実測でデータ化しているので、その観点でも嬉しいはずです。

また、画質についても今回改めて比較実験をした結果超優れているということが分かりました。

TinyPNGの削減効果もときには90%を超えることがありかなり優秀だと思っていましたが、Webpは平均でそれを上回る圧縮率を持ちながら目に見える画像劣化がマジでほとんどない模様。

みるみ
みるみ

TinyPNGも「人の目で差は分からないだろう」と思っていたのだけど、実は今回たまたま特定の条件ではすごく目立つ劣化が起こることを発見しました。本当にたまたまなのだけど。

というわけで、今回の施策はかなり良さそうです。

結果のデータないの?

ねぇんだわ。(ド無能)

これ含め色々高速化の施策を同時に行ったりサイト構造を変えたりしていたので、もう何をどこでやったんだか行方不明です。本当に僕はバカだなといつも自分で思います。

まあ表示速度は体感でかなり速くなった気がするし、PageSpeed Insightsのフィールドデータもちょっとずつ数字が良くなり始めてます。

どうせ誰も読んでないだろうし、許して…。

おわりに

まあ言わずもがななんですけど、プラグインが増えることに抵抗がない人は EWWW Image Optimizer を使えばよいです。

僕はもともとあのプラグインが好きではないというのも今回のきっかけのひとつでした。

例によって需要が針のように細い記事ですが、どなたかのお役に立てましたら幸いでございます。

みるみ
Follow Me!
みるみ

雑記ブロガー。文章を書くのが大好き。WordPress制作やカスタマイズはもちろん、Webアプリ/サービスも作ります。

このブログ「みるめも」が初めての方ははじめましての10記事がおすすめ。もやってます。

みるめも