Gulp 5で画像容量を圧縮

更新:2025-01-18
JavaScript
概要
PNG・JPG・SVGの容量をGulp 5で圧縮する。以下の構成を想定。
├── dist│ ├── ここに圧縮後の画像を生成│ └── hoge│ └── ディレクトリ構造は維持して生成└── src ├── ここに元画像を配置 └── hoge └── ディレクトリも切れるようにする
プロジェクトの作成
package.jsonを適当に作成。
{ "name": "gulp-compress-image", "private": true, "version": "0.0.0", "type": "module", "scripts": {}, "devDependencies": {}}
開発環境の整備
モジュールの追加
npm i -D del gulp gulp-imagemin imagemin-pngquant svgo through2
モジュール名 | 簡易説明 |
---|---|
del | ディレクトリやファイルの削除。 |
gulp | タスクランナー。 |
gulp-imagemin | 画像圧縮プラグイン。 |
imagemin-pngquant | PNG専用の画像圧縮プラグイン。 |
svgo | SVGの最適化。 |
through2 | カスタムストリーム作成用。 |
gulp-imagemin
あたりは脆弱性があると表示されるが、ローカルで使用するだけなので問題なし。
Node.jsのバージョン
どうもNode.jsがバージョン21より新しいとエラーになる。
Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla. at ModuleJobSync.runSync (node:internal/modules/esm/module_job:393:13) at ModuleLoader.importSyncForRequire (node:internal/modules/esm/loader:335:47) at loadESMFromCJS (node:internal/modules/cjs/loader:1570:24) at Module._compile (node:internal/modules/cjs/loader:1722:5) at Object..js (node:internal/modules/cjs/loader:1905:10) at Module.load (node:internal/modules/cjs/loader:1474:32) at Function._load (node:internal/modules/cjs/loader:1286:12) at TracingChannel.traceSync (node:diagnostics_channel:322:14) at wrapModuleLoad (node:internal/modules/cjs/loader:234:24) at Module.require (node:internal/modules/cjs/loader:1496:12) { code: 'ERR_REQUIRE_ASYNC_MODULE'}
バージョン21で固定する。
volta install node@21.7.3volta pin node@21.7.3
バージョンを確認してみるとよく分からないエラーに遭遇。
node -v
Error: proto::tool::required
× This project requires Node.js 21.7.3 (detected from .../package.json), but this version has │ not been installed. Install it with proto install node 21.7.3, or enable the auto-install setting to automatically install missing │ versions!
書いてあるとおりproto install node 21.7.3
したら解消した。
雑記
出力先の掃除
del
を使用して過去に生成した画像を消すようにした。置いておく意味もないので。
画像が壊れる問題
Gulp 5の場合、src
のencodingを無効にする必要があるらしい。
https://stackoverflow.com/questions/78730067/gulp-imagemin-breaking-images-and-not-optimizing
PNGの圧縮
PNGはgulp-imagemin
のoptipng
でも圧縮できるみたいだが、そこで処理が止まってしまう?ので、かわりにimagemin-pngquant
を使用した。
SVGの圧縮
imagemin
のsvgo()
でだいぶ圧縮されるが、id
やdata-name
といった属性が残っていた。
<?xml version="1.0" encoding="UTF-8"?><svg id="_レイヤー_2" data-name="レイヤー 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800"> ...</svg>
svgo()
のオプションを指定する方法がよく分からなかったので、直接svgo
をインストールして使うことにした。
圧縮率の表示
through2
を使用して、圧縮率をコンソールに出力するようにした。
- jpg/sample.jpg 953293 bytes -> 602399 bytes 36%削減 - png/sample.png 5726530 bytes -> 1908340 bytes 66%削減 - svg/sample.svg 774 bytes -> 496 bytes 35%削減
コンソールの色付けは以下を参考にした。
https://qiita.com/shuhei/items/a61b4324fd5dbc1af79b
成果
https://github.com/namakurajin/compress-image-with-gulp-5
// 必要なモジュールimport { src, dest } from 'gulp';import imagemin, { mozjpeg, svgo } from 'gulp-imagemin';import pngquant from 'imagemin-pngquant';import { optimize } from 'svgo';import through2 from 'through2';import { deleteSync } from 'del';
// コンソールの色付けvar green = '\u001b[32m';var reset = '\u001b[0m';
export default () => ( deleteSync(['dist/**']), // まず、出力先を空にしておく src('src/**/*.{jpg,png,svg}', { encoding: false }) // 画像を圧縮 .pipe( imagemin([ mozjpeg({quality: 40, progressive: true}), pngquant({ quality: [0.5, 0.6], speed: 1 }), svgo() ]) ) // SVGからdata-name属性を除去 .pipe(through2.obj((file, _enc, cb) => { const result = optimize(file.contents.toString(), { plugins: [ { name: 'removeAttrs', params: { attrs: ['data-name'] } } ] }); file.contents = Buffer.from(result.data); cb(null, file); })) // 圧縮率を表示 .pipe(through2.obj((file, _enc, cb) => { const originalSize = file.stat.size; const compressedSize = file.contents.length; const compressionRatio = Math.floor((originalSize - compressedSize) / originalSize * 100); console.log(` - ${green} ${file.relative} ${reset} ${originalSize} bytes -> ${compressedSize} bytes ${green} ${compressionRatio}%削減 ${reset}`); cb(null, file); })) // 出力 .pipe(dest('dist')));
続く...かもね