パフォーマンスをなるべく落とさない、Habanero Beeにおける画像ホスティング戦略
前回、前々回と、自身が作成したHabanero BeeというAMP対応のサイトを簡単に作成できるツールについて書かせていただきましたが、今回もHabanero Beeの内部実装や設計などについて書かせていただきます。
(一度に書ける量ではないため、複数回に分けて書かせていただいています)
今回は前回の記事を書いた時に少し触れた画像、そして amp-img
タグの生成について書かせていただきます。
amp-imgタグとは
そもそもamp-img
タグとは? というところからですが、AMP対応のサイトを作成する場合、HTMLのimg
タグは使用できません。
代わりにAMP対応のサイトでは amp-img
というタグを使います。
概要は下記のドキュメントが詳しいので、是非ご覧になってみてください。
そして上のドキュメントにも書かれている通り、amp-img
タグを利用するには、画像サイズの記述が必要になります。
具体的には下記のようなコードを書く必要があるのです。
(公式のexampleを引用しております)
<amp-img alt="A view of the sea"
src="/static/inline-examples/images/sea.jpg"
width="900"
height="675"
layout="responsive">
</amp-img>
Habanero BeeではMarkdownを用いてコンテンツを記述することが可能ですが、markdownで画像を記述する場合、下記のようになります。
![A view of the sea](/static/inline-examples/images/sea.jpg)
皆さんも御存知のように、素のmarkdownでは画像のサイズを指定することができませんし、そもそもコンテンツを記載する上でなるべく文法を拡張するようなこともしたくありませんでした。
img2amp-imgを用いてHTMLのimgタグをamp-imgに変換する
ただ、amp-img
タグでは画像のサイズは必須項目(※)のため、急遽下記のようなnpmライブラリを作り、対応していくことにしました。
※厳密に言うと必須ではありません。指定しなくてもエラーにならないケースもあるにはありますが、レスポンシブデザインを意識した画像の配置をイメージする場合、画像サイズの指定は実質必須となります。
リポジトリはこちらです。
このツールはサーバサイド環境で動きます。
例えば下記のような、Node.jsで動くコードを書きます。
const img2AmpImg = require('img2amp-img');
(async () => {
const imageTag = '<img src="https://dummyimage.com/200x100" alt="sample image" />';
const ampImgTag = await img2AmpImg(imageTag);
console.log(ampImgTag);
})();
このコードの実行結果は下記です。
<amp-img
alt="sample image"
src="https://dummyimage.com/200x100"
width="200"
height="100"
layout="responsive"
></amp-img>
実行結果を見ると、指定した画像のwidth
とheight
が取れています。
これはimg2amp-img
側で一度指定した画像をfetchして画像のサイズを取得しているからです。
img2amp-img
がやってくれることは凄くシンプルで、指定された画像のサイズを取得し、そのサイズを格納した上で amp-img
タグを返すということだけです。
Layoutオプションについて
また amp-img
タグにはlayout
という属性が用意されています。
詳細は上に貼ったamp-img
タグを参照していただくのが早くて正確だと思いますが、一応img2amp-img
でもこちらのレイアウトオプションには対応しています。
Layoutオプションについては下記の種類が利用可能です。
'responsive'
'fill'
'fixed'
'fixed-height'
'flex-item'
'intrinsic'
'nodisplay'
例えば、下記のようなコードを実行した場合、
const img2AmpImg = require('img2amp-img');
(async () => {
const imageTag = '<img src="https://dummyimage.com/200x100" alt="sample image" />';
const ampImgTag = await img2AmpImg(imageTag, 'fixed-height');
console.log(ampImgTag);
})();
以下の結果が返されます。
<amp-img
alt="sample image"
src="https://dummyimage.com/200x100"
width="auto"
height="100"
layout="fixed-height"
></amp-img>
Habanero Bee自体は現在採用しているページデザインの関係上、layout=responsive
しか使用しませんが、今後ページテンプレートを追加していくに従って、ここについては再考の余地がありそうかと考えています。
Habanero Beeでの画像ホスティング戦略
さて、前々回に書いた投稿でも触れていますが、Habanero Beeにはコンテンツを管理するためのダッシュボードは用意されておらず、Google スプレッドシート 1ファイルのみでサイト内のすべてのコンテンツを管理するようになっています。
当然画像の管理についてはノータッチであり、現在のところ画像を貼りたい場合は imgur のような、画像を別に置いておけるような場所に一度アップロードした上で、その画像のURLを貼って利用するような形となります。
ただ、サイトのパフォーマンス的には、ページを開くたびにいちいち外部URLに画像を取得しにいっていたのではパフォーマンス的にもあまり良くなさそうです。
サイトに利用している画像数が少ないうちはそれほど影響はなさそうですが、サイトの規模が大きくなってきた時にこの影響は鮮明に現れてきそうだったため、前回のこちらでも書いたように、サイトのビルドフェーズの間に、用いられている画像のダウンロードを行い、Habanero Beeサイト内に取り込んだ上で、そこのURLを参照する、という処理を挟むことにしました。
また、同じドメインで管理することで、サイト間の依存を少しでも減らし、すべての責務を自ドメイン側で受け止めることができるようになるので、結果的にサイト表示の安定性を増すことにつながっているかと思います。
(現在の設計的にはそれでも、Habanero Beeのホスティング先として指定しているNetlifyになにか問題が生じた場合は影響を受けますが、ここは後々の課題。)
実装
具体的には下記のような処理を書いています。
参照している実際のソースはこちら
const downloadImage = async (url: string): Promise<string> => {
const response = await fetch(url);
// @ts-ignore
const buffer = await response.buffer();
const hashedImageName = getHashImageName(url);
const imagePath = `/images/${hashedImageName}`;
// save image
await fs.writeFile(`./public${imagePath}`, buffer);
return imagePath;
};
一度画像をダウンロードし、public
配下に保存しています。
Habanero BeeはNext.jsをベースにしたプロジェクトのため、このように保存することでNext.js側から参照することができるようになります。
以上のようにして、一度画像をプロジェクト内に保存した後、画像を参照しています。
パフォーマンス比較
この施策についてはかなり微量ではあるもののパフォーマンスの向上にもつながっており、このような比較結果が出ています。
計測サイトの規模が小さかったというのもパフォーマンス改善率が少なかった一つの要因かもしれません。
ただ、自身の別プロジェクトでHabanero Beeを採用し運用しているサイトでは、実際に一覧画面に50枚の画像が並んでいるページを表示させてもLighthouseでは以下のようなスコアが出せています。
ただ、ここについては上の施策が影響しているというよりも、amp自体にすでに備わっている遅延読み込み(Lazy Load)の効果が効いているというのが一番の要因かとは思います。
当然といえば当然ですが、やはり遅延読み込みがあると、サイトの使用感の向上に繋がります。
(AMPを取り入れた場合、この遅延読み込みはAMP側で行ってくれるため、そのための実装は必要ありません。)
いずれにせよ、コンテンツをそれなりに増やしてもそれほど影響がないのは嬉しい限りです。
というわけで、今回はHabanero Beeの画像に関する部分を書かせていただきました。
現在は画像サイズを取得して amp-img
タグを生成していますが、ここは指定されている画像のサイズに応じて、画像のサイズのリサイズを動的に行うようにするなどまだまだ改善点はあります。
最後になりますが、今回ご紹介させていただいた、img2amp-imgも、もし機会があれば利用してみてください。
勿論PRもお待ちしていますm(_ _)m
Discussion