⚒️
npm scriptsで画像を最適化する(WebP変換とファイル監視を含む)
要件
- srcディレクトリにある画像を対象に最適化する
- 最適化した画像は構造を変えずhtdocsディレクトリに出力する
- jpgとpngはWebPへの変換に対応する
- WebPのフォールバックが必要な案件に備えてjpgとpngも同時に出力できるようにする
- WebPを使わない
- WebPを使うけど、フォールバック画像が必要
- フォールバック画像は不要で、WebPを使う
パッケージをインストールする
画像最適化に必要なパッケージをインストールします。
npm i -D imagemin-keep-folder imagemin-mozjpeg imagemin-pngquant imagemin-gifsicle imagemin-svgo imagemin-webp
imageminだとディレクトリ構成が無視されてしまうIssueが挙がっています(マージされている気配はなさそう?)。
今回は「imagemin-keep-folder」で解消しています。
クロスプラットフォームに対応して簡潔に記述できるnpm-run-allとファイルの監視機能があるonchangeもインストールします。
npm i -D npm-run-all onchange
画像最適化の実行ファイルを作成する
scripts/imagemin.mjs
を作成、以下のようにしています。
quality
はプロジェクトに合わせて調整してください。
scripts/imagemin.mjs
import imagemin from 'imagemin-keep-folder';
import imageminMozjpeg from 'imagemin-mozjpeg';
import imageminPngquant from 'imagemin-pngquant';
import imageminGifsicle from 'imagemin-gifsicle';
import imageminSvgo from 'imagemin-svgo';
import imageminWebp from 'imagemin-webp';
/**
* WebPの使用とフォールバックのモードを指定します。
* - 'noWebp': WebPを使用せず、元の画像のみ出力
* - 'useIfPossible': WebPを使用する、WebPに変換できない元画像は出力する
* - 'fallback': WebPを使用する、元の画像も出力
*/
optimizeImages('useIfPossible');
/**
* WebPに変換可能な画像を最適化します。
*/
async function optimizeWebpConvertibleImages() {
await imagemin(['src/**/*.{jpg,jpeg,png}'], {
use: [
imageminMozjpeg({ quality: 85, progressive: true }),
imageminPngquant({ quality: [0.85, 0.9] }),
],
replaceOutputDir: output => output.replace(/src\//, 'htdocs/')
});
}
/**
* WebPに変換不可能な画像を最適化します。
*/
async function optimizeNonWebpImages() {
await imagemin(['src/**/*.{gif,svg}', '!src/assets/svg/*.svg'], {
use: [
imageminSvgo(),
imageminGifsicle(),
],
replaceOutputDir: output => output.replace(/src\//, 'htdocs/')
});
}
/**
* WebP画像を生成します。
*/
async function generateWebpImages() {
await imagemin(['src/**/*.{jpg,jpeg,png}'], {
use: [
imageminWebp({ quality: 70 }),
],
replaceOutputDir: output => output.replace(/src\//, 'htdocs/')
});
}
/**
* 画像を最適化します。
* @param {string} webpMode - WebPの出力モード ('noWebp', 'useIfPossible', 'fallback')
*/
async function optimizeImages(webpMode) {
switch (webpMode) {
case 'noWebp':
await optimizeWebpConvertibleImages();
await optimizeNonWebpImages();
break;
case 'useIfPossible':
await generateWebpImages();
await optimizeNonWebpImages();
break;
case 'fallback':
await optimizeWebpConvertibleImages();
await generateWebpImages();
await optimizeNonWebpImages();
break;
default:
throw new Error('Invalid webpMode');
}
}
optimizeImages()
の引数に任意の文字列を渡すと、出力形式を切り替えられます。
-
noWebp
: WebPを使用せず、元の画像のみ出力 -
useIfPossible
: WebPを使用する、WebPに変換できない元画像は出力する -
fallback
: WebPを使用する、元の画像も出力
IEを無視できる環境であればuseIfPossible
でOKです。
jpgとpngはWebpに変換、svgとgifは変換できないので最適化して出力します。
npm scriptsを設定する
HTMLやCSSなどのスクリプトを載せていないのですが、画像処理に関することだけ抜き出しています。
package.json
"scripts": {
"start": "run-s -c dev watch",
"build": "npm-run-all -s clean -p build:*",
"watch": "run-p watch:*",
"watch:image": "onchange \"src/**/*.{jpg,jpeg,png,gif,svg}\" -- npm run dev:image",
"dev": "run-p dev:*",
"dev:image": "NODE_ENV=development run-s -c image:*",
"build:image": "NODE_ENV=production run-s -c image:*",
"image:min": "node scripts/imagemin.mjs",
"clean": "run-p clean:*",
"clean:htdocs": "shx rm -rf \"htdocs\" && shx echo \"Done cleaning\""
},
-
image:min
で画像の最適化を実行します -
dev
は開発用のビルド、build
は本番用のビルドとしています -
run-s
は順番に実行、-c
はエラーが起きても最後の処理まで止まらずに実行するオプションです -
run-p dev:*
やnpm-run-all -s clean -p build:*
のようにして、他のタスクを含めてglobパターンで実行します(clean
はhtdocsディレクトリを削除するスクリプト) -
onchange \"src/**/*.{jpg,jpeg,png,gif,svg}\" -- npm run dev:image
で画像ファイルを監視します -
run-s -c dev watch
とすることで、開発用ビルドを順番に実行し、完了したら監視状態になります -
htdocs
ディレクトリの削除にはshx(インストールが必要です) を使用しています
Discussion
webpや画像最適化の自動化で悩んでいたところ、こちらの記事を見つけてとても嬉しかったです!
一点だけご質問させていただきます。
src
の画像削除してもhtdocs
の画像は削除されなくて"build": "npm-run-all -s clean -p build:*",
を実行してみたら
ERROR: Task not found: "clean"
と表示されました。
cleanタスクは何を設定したらいいでしょうか?
お忙しいところ恐縮ですが、何卒よろしくお願いいたします。
お役に立ててよかったです!
cleanに関してnpm scriptsを設定するに追記しました。
shxというライブラリでディレクトリを削除していますが、別のライブラリでも問題ありません。
ご回答いただきありがとうございます!
とても勉強になりました!