このサイトはそこまで画像を多用しているサイトではないですが、画像を最適化することはページ読み込み、ひいてはSEO上でも重要になってきています。当サイトでは、node.jsのsharp, imageminというライブラリとNetlifyのビルド設定を用いて画像の最適化を自動で処理するようにしています。
この記事ではimageminの細かい使い方などは解説していませんので、具体的な方法などは「imagemin 使い方」などで検索してください。
画像最適化の手法
当サイトで使っている画像最適化手法は
- 初期表示画像の縮小(元画像の半分)
- 画像ファイルそのものの軽量化(圧縮)
初期表示画像は遅延読み込みを使って、スクロール時に本画像へ差し替えます。詳しくはこちらの『markdown-itをカスタマイズして遅延ロードを実装する』に書いてます。
基本構成と処理の流れ
以下のようなフォルダ構成になっています。(実際はもうちょっと細かい構成になっています)
┬─ images
└─ images_src
上記のimages_src
フォルダが元画像、images
フォルダが実際にページに表示される画像になります。画像を作成したら、このimages_src
フォルダに格納します。その状態で最適化処理にかければimages
フォルダへ画像が出力されます。
最適化処理は次のような流れで実行されます。
- 元画像を
images
フォルダへコピー - 画像を半分に縮小したものを作成
- 縮小された画像、コピーされた元画像を圧縮軽量化
画像のコピー
まずは元画像をコピーします
const fs = require("fs-extra");
const path = require("path");
const src_image_path = path.resolve(__dirname, "PATH/TO/images_src");
const dist_image_path = path.resolve(__dirname, "PATH/TO/images");
await fs.copySync(src_image_path, dist_image_path);
画像のリサイズ
画像のリサイズにはSharpというライブラリを使っています。
const sharp = require("sharp");
function get_file_extension(format) {
switch (format) {
case "jpeg":
return "jpg";
break;
default:
return format;
}
}
const name = "FILE_NAME.jpg"
const sharpen_image = await sharp("PATH/TO/IMAGE");
const image_meta = await sharpen_image.metadata();
const extension = get_file_extension(image_meta.format);
const file_name = name.replace(`.${extension}`, "");
await sharpen_image
.resize(Math.round(image_meta.width * 0.5))
.toFile(`PATH/TO/OUTPUT/${file_name}_half.${extension}`)
sharpにファイルを投入しリサイズした後、ファイル名の末尾に_half
の文字を追加して保存しています。(なんか、もっと良いやり方がある気もする…)
ここではconst name
という形で定数を代入していますが、実際にはfs
などの処理で動的に対象ファイルのファイル名を入れています。
画像の圧縮
縮小版ができたので、画像を圧縮しています。画像の圧縮には各種「imagemin」のプラグインを使っています。
const imagemin = require("imagemin-keep-folder");
const imageminMozjpeg = require("imagemin-mozjpeg");
const imageminPngquant = require("imagemin-pngquant");
const imageminGifsicle = require("imagemin-gifsicle");
const imageminSvgo = require("imagemin-svgo");
await imagemin(["PATH/TO/images/**/*.{jpg,png,gif,svg}"], {
plugins: [
imageminMozjpeg({ quality: 80 }),
imageminPngquant({ quality: [0.95, 1] }),
imageminGifsicle(),
imageminSvgo()
]
});
ここでの圧縮は非可逆圧縮です。なので、元の画像からは劣化していますが、見た目には影響のないレベルのクオリティ設定にしています。体感的にはAdobe Illustratorで描きだした元画像の20~40%くらい削減される気がしてます。
この処理を経て、images
フォルダ内の画像が圧縮されます。
処理をまとめる
const fs = require("fs-extra");
const path = require("path");
const resize = require("resize");
const image_compress = require("compress");
const src_image_path = path.resolve(__dirname, "PATH/TO/images_src");
const dist_image_path = path.resolve(__dirname, "PATH/TO/images");
await resize.exec();
await image_compress.exec();
await fs.copySync(src_image_path, dist_image_path);
上記のリサイズと圧縮の処理をexport
して、コピーの処理と合体させて一つのjsにまとめました。package.jsonのnpm scriptsに以下を追加しておきます。
"scripts": {
"image-optimize": "node image_optimize.js"
}
Netlifyデプロイ設定
Netlifyのデプロイ設定のBuild commandにyarn run image-optimize && yarn run generate
を設定します(自分はパッケージマネージャーにyarnを使っているのでyarnコマンドですが、npmを使っている方はnpmを使ってください)
yarn run generate
の方はNuxtのgenerateコマンドです。画像最適化したのちにgenerateして静的ファイルを生成しています。
これでNetlifyデプロイ時に画像最適化処理を自動でやってくれます。
さいごに
面倒くさい処理はどんどん機械にまかせてしまいましょう。