圧縮とエンコードの自動化

高性能の画像ソースの生成を、開発プロセスのシームレスな部分に組み込みましょう。

画像データのエンコードから、レスポンシブ画像を支える情報密度の高いマークアップまで、このコースで紹介するすべての構文は、マシンがマシンと通信するための手段です。クライアント ブラウザがニーズをサーバーに伝える方法と、サーバーが現物で応答するための方法をいくつか発見しました。レスポンシブ画像のマークアップ(特に srcsetsizes)を使用すると、かなりの量の情報を比較的少ない文字で記述できます。良くも悪くも、この簡潔さは設計によるものです。これらの構文を簡潔にし、デベロッパーが解析しやすくすることで、ブラウザによる解析が難しくなることがあります。文字列が複雑になるほど、パーサーでエラーが発生する可能性や、ブラウザ間で動作に意図しない違いが生じる可能性が高まります。

自動画像エンコード ウィンドウ。

とはいえ、こうした被写体が威圧感を感じるような特徴もあるため、解決策を提供することもできます。マシンが読みやすい構文は、機械によってより簡単に記述できるということです。ウェブのユーザーであれば、画像エンコードと圧縮の自動圧縮の例をたくさん目にしたことがあるはずです。ソーシャル メディア プラットフォーム、コンテンツ管理システム(CMS)、メール クライアントからウェブにアップロードした画像はすべて、サイズ変更、再エンコード、圧縮を行うシステムを通過することはほぼ間違いありません。

同様に、プラグイン、外部ライブラリ、スタンドアロンのビルドプロセス ツール、クライアントサイド スクリプトの責任ある使用のいずれを通じても、レスポンシブ画像マークアップは自動化に容易に役立ちます。

画像のパフォーマンスの自動化に関する主な懸念事項は、画像作成の管理(エンコード、圧縮、srcset 属性の入力に使用する代替ソース)と、ユーザー向けのマークアップの生成の 2 つです。このモジュールでは、最新のワークフローの一部として画像を管理する一般的なアプローチをいくつか説明します。開発プロセスの自動化フェーズとして、サイトを強化するフレームワークやコンテンツ マネジメント システムを通じて行う場合や、コンテンツ配信ネットワークをほぼ完全に抽象化する場合もあります。

圧縮とエンコードの自動化

プロジェクトで使用する画像のそれぞれについて、理想的なエンコードと圧縮レベルを手動で決める時間を確保できることはまずまずありません。画像転送のサイズをできる限り小さくしておくことも重要ですが、本番環境ウェブサイト向けのすべての画像アセットについて、圧縮設定を微調整して代替ソースを再保存すると、日々の作業で大きなボトルネックが発生します。

さまざまな画像形式と圧縮タイプで説明したように、画像の最も効率的なエンコードはコンテンツによって常に決まります。また、レスポンシブ画像で説明したように、画像ソースに必要な代替サイズは、ページ レイアウトで画像が占有する位置によって決まります。最新のワークフローでは、これらの決定に個別ではなく全体的にアプローチします。つまり、画像が使用される状況に最適な適切なデフォルトのセットを決定します。

写真画像のディレクトリのエンコードを選択する場合、品質と転送サイズに関しては AVIF が明らかに優れていますが、サポートは限られています。WebP は最適化された最新のフォールバックを提供し、JPEG は最も信頼性の高いデフォルトです。ページ レイアウトでサイドバーに配置される画像用に生成する必要がある代替サイズは、最上位のブレークポイントでブラウザのビューポート全体に表示される画像と大きく異なります。圧縮設定では、複数の結果ファイルについて、ぼかしや圧縮によるアーティファクトの発生を考慮する必要があります。これにより、ワークフローの柔軟性と信頼性を高めるために、各画像から考えられるすべてのバイトを切り分けるスペースが減ります。まとめると、このコースで学んだのと同じ意思決定プロセスを 大まかに利用することになります。

処理自体に関しては、画像を一括変換、変更、編集するメソッドを提供するオープンソースの画像処理ライブラリが多数存在し、速度、効率性、信頼性が競合しています。これらの処理ライブラリを使用すると、画像編集ソフトウェアを開くことなく、エンコードと圧縮の設定を画像のディレクトリ全体に適用できます。また、それらの設定をその場で調整する必要がある場合に、元の画像ソースを保持できます。ローカル開発環境からウェブサーバー自体まで、さまざまなコンテキストでの実行を想定しています。たとえば、圧縮が中心となる Node.js の ImageMin は、一連のプラグインを使用して特定のアプリケーションに合わせて拡張できます。一方、クロス プラットフォームの ImageMagick と Node.js ベースの Sharp には、驚くほど多くの機能が備わっています。

これらの画像処理ライブラリを使用すると、デベロッパーは標準の開発プロセスの一環として画像をシームレスに最適化する専用のツールを構築できます。これにより、オーバーヘッドを最小限に抑えてプロジェクトが常に本番環境に対応した画像ソースを参照できます。

ローカルでの開発ツールとワークフロー

Grunt、Gulp、Webpack などのタスクランナーやバンドラを使用すると、CSS や JavaScript の圧縮など、他の一般的なパフォーマンス関連タスクとともに画像アセットを最適化できます。比較的単純なユースケースを考えてみましょう。プロジェクトのディレクトリには、一般公開ウェブサイトで使用することを想定した写真画像が 10 枚以上含まれています。

まず、これらの画像を効率的かつ一貫してエンコードする必要があります。前のモジュールで学習したように、WebP は画質とファイルサイズの両方において、写真画像の効率的なデフォルトです。WebP は十分にサポートされていますが、普遍的にサポートされているわけではないため、プログレッシブ JPEG 形式でのフォールバックも含める必要があります。次に、srcset 属性を使用してこれらのアセットを効率的に配信するために、エンコードごとに複数の代替サイズを生成する必要があります。

画像編集ソフトウェアで行うと、これは反復的かつ時間のかかる面倒な作業になりますが、Gulp のようなタスクランナーは、この種の反復を正確に自動化するように設計されています。Sharp を使用する gulp-responsive プラグインは、どれも同様のパターンに従う多くのオプションの一つです。つまり、ソース ディレクトリ内のすべてのファイルを収集し、再エンコードして、画像形式と圧縮で学んだのと同じ標準化された「品質」の短縮形に基づいて圧縮します。結果のファイルは、定義したパスに出力され、元のファイルはそのままで、ユーザー向けの img 要素の src 属性で参照できます。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.webp = function() {
  return src('./src-img/*')
    .pipe(respimg({
      '*': [{
        quality: 70,
        format: ['webp', 'jpeg'],
        progressive: true
      }]
  }))
  .pipe(dest('./img/'));
}

このようなプロセスがあれば、プロジェクトの誰かが、元の画像ソースを含むディレクトリに、大規模なトゥルーカラーの PNG としてエンコードされた写真を誤って追加しても、本番環境に悪影響が及ぶことはありません。元の画像のエンコードに関係なく、このタスクにより、効率的な WebP と信頼性の高いプログレッシブ JPEG フォールバックが生成され、オンザフライで簡単に調整できる圧縮レベルで生成されます。もちろん、このプロセスにより、元の画像ファイルがプロジェクトの開発環境に確実に保持されます。つまり、これらの設定はいつでも調整でき、自動出力のみが上書きされます。

複数のファイルを出力するには、複数の構成オブジェクトを渡します。ただし、width キーとピクセル単位の値を除きます。

const { src, dest } = require('gulp');
const respimg = require('gulp-responsive');

exports.default = function() {
  return src('./src-img/*')
    .pipe(respimg({
    '*': [{
            width: 1000,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-1000' }
            },
            {
            width: 800,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-800' }
            },
            {
            width: 400,
            format: ['jpeg', 'webp'],
            progressive: true,
            rename: { suffix: '-400' },
        }]
        })
    )
    .pipe(dest('./img/'));
}

上記の例では、元の画像(monarch.png)は 3.3 MB を超えています。このタスクによって生成された最大ファイル(monarch-1000.jpeg)は約 150 KB です。最小の monarch-400.web は 32KB です。

[10:30:54] Starting 'default'...
[10:30:54] gulp-responsive: monarch.png -> monarch-400.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-800.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.jpeg
[10:30:54] gulp-responsive: monarch.png -> monarch-400.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-800.webp
[10:30:54] gulp-responsive: monarch.png -> monarch-1000.webp
[10:30:54] gulp-responsive: Created 6 images (matched 1 of 1 image)
[10:30:54] Finished 'default' after 374 ms

もちろん、圧縮によるアーティファクトの目に見える結果を慎重に調査するか、場合によっては圧縮率を高めて費用をさらに削減する必要があります。このタスクは破壊的ではないため、これらの設定は簡単に変更できます。

言わば、手動でのマイクロ最適化で数キロバイトを解放することで、効率だけでなく復元性も備えたプロセスを実現できます。これは、高パフォーマンスの画像アセットに関する知識を、手動で介入することなく、プロジェクト全体にシームレスに適用するツールです。

レスポンシブ画像のマークアップの実例

srcset 属性の入力は通常、簡単な手動プロセスです。この属性は、実際にはソースの生成時にすでに行った構成に関する情報のみをキャプチャします。上記のタスクでは、属性が従うファイル名と幅情報を確立しました。

srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w"

srcset 属性の内容は規範的ではなく、説明的な内容です。すべてのソースのアスペクト比が変わらない限り、srcset 属性をオーバーロードしても問題はありません。srcset 属性には、不要なリクエストを発生させることなく、サーバーによって生成されたすべての代替カットの URI と幅を含めることができます。また、レンダリングされた画像に対して提供する候補のソースが多いほど、ブラウザでリクエストをより効率的に調整できるようになります。

レスポンシブ画像で説明したように、<picture> 要素を使用して WebP または JPEG のフォールバック パターンをシームレスに処理することをおすすめします。この場合、type 属性を srcset と組み合わせて使用します。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

すでに学習したように、WebP をサポートするブラウザは type 属性の内容を認識し、その <source> 要素の srcset 属性を画像候補のリストとして選択します。image/webp を有効なメディアタイプとして認識できないブラウザは、この <source> を無視して、代わりに内部 <img> 要素の srcset 属性を使用します。

ブラウザのサポートに関してもう 1 つ考慮すべき点があります。レスポンシブな画像マークアップをサポートしていないブラウザでも代替機能が必要であり、特に古いブラウジング環境では、画像が破損するリスクがあります。こうしたブラウザでは、<picture><source>srcset がすべて無視されるため、内部の <img>src 属性でデフォルトのソースを指定します。

画像の下方へのスケーリングは視覚的にシームレスに行われ、JPEG エンコードは普遍的にサポートされているため、最大サイズの JPEG を選択することをおすすめします。

<picture>
  <source type="image/webp" srcset="filename-1000.webp 1000w, filename-800.webp 800w, filename-400.webp 400w">
  <img src="filename-1000.jpg" srcset="filename-1000.jpg 1000w, filename-800.jpg 800w, filename-400.jpg 400w" sizes="…" alt="…">
</picture>

sizes はもう少し扱いにくいかもしれません。学習したように、sizes は必然的にコンテキストに応じて決まります。レンダリングされたレイアウトで画像が占有するスペースがわからなくても、属性を設定することはできません。リクエストを効率的に行うためには、ページ レイアウトを規定するスタイルがリクエストされるずっと前の、エンドユーザーがリクエストを行う時点で、正確な sizes 属性をマークアップに含める必要があります。sizes を完全に省略すると、HTML 仕様に違反するだけでなく、sizes="100vw" と同等のデフォルト動作が発生します。つまり、この画像はビューポートによってのみ制限されることがブラウザに通知され、可能な限り最も大きい候補ソースが選択されます。

特に負担の大きいウェブ開発タスクと同様に、sizes 属性を手書きするプロセスを抽象化するツールが多数作成されています。respImageLint は、sizes 属性の正確性を精査し、改善するための提案を行う、不可欠なコード スニペットです。これは、ブラウザ内で実行するブックマークレットとして実行され、画像要素を含む完全にレンダリングされたページを指します。ブラウザがページ レイアウトを完全に理解している状況では、考えられるすべてのビューポート サイズで、そのレイアウトで画像が占有するスペースをピクセル単位でほぼ完全に認識できます。

サイズまたは幅の不一致を示すレスポンシブ画像レポート。

sizes 属性の lint チェックを行うツールも確かに便利ですが、この属性を大量に生成するためのツールとして、さらに多くの価値があります。 ご存じのように、srcsetsizes の構文は、画像アセットのリクエストをシームレスに最適化することを目的としています。本番環境では一切使用すべきではありませんが、ローカル開発環境でページのレイアウトを作成する際には、デフォルトの sizes プレースホルダの値 100vw でまったく妥当です。レイアウト スタイルを設定したら、respImageLint を実行すると、カスタマイズされた sizes 属性が提供されます。手書きで書くよりもはるかに詳細なレベルでこれをコピーしてマークアップに貼り付けることができます。

推奨されるディメンションを含むレスポンシブ画像レポート。

サーバーでレンダリングされたマークアップによって開始された画像リクエストは、すばやく実行され、JavaScript でクライアント側の sizes 属性を生成できますが、クライアント側でリクエストが開始された場合、これは当てはまりません。たとえば、Lazysizes プロジェクトでは、レイアウトが確立されるまで画像リクエストを完全に延期できるため、JavaScript で sizes 値を生成できるため、非常に便利になり、ユーザーにとっては可能な限り効率的なリクエストが保証されます。ただし、このアプローチでは、サーバーでレンダリングされるマークアップの信頼性とブラウザに組み込まれた速度最適化が犠牲になることにご注意ください。ページがレンダリングされた後にのみこれらのリクエストを開始すると、LCP スコアに非常に大きな悪影響を及ぼします。

もちろん、React や Vue などのクライアント側のレンダリング フレームワークにすでに依存している場合、それはもう負債です。そのような場合、Lazysizes を使用すると sizes 属性をほぼ完全に抽象化できます。さらに良い点として、遅延読み込み画像の sizes="auto" がコンセンサスとネイティブ実装を得るようになるため、Lazysizes は実質的に新たに標準化されたブラウザ動作のポリフィルになります。