どうも上かるびです。
現在タスクランナーとしてgulpを使用しているのですが、jsを書く機会が増えてきたのでjsのバンドル・構文チェックだけwebpackに任せて、別々で動かしている感じです。

今までは「画像もバンドルする必要あるのか?」とか「gulpで事足りるなら今のままでいいかな」とか考えてました。
が、webpackの知識をインプットをしているうちに
「監視・リロードもできるんかい」とか
「ESLintとかPrettier併用するならwebpack一択やん」とか
「gulp+webpackの合わせ技だと今後面倒になるかも」とか
思い始めました。
そこで他の方はどんな感じでやっているのか、Twitterでアンケートを取ってみました。
Twitterでのタスクランナー世論調査
【質問】
Webデザイナーさん含め、フロントのコーディングをしているコーダーさんに質問です。タスクランナーはどのように使用していますか?#駆け出しエンジニアと繋がりたい— 上かるび@フロントエンド (@jookalubi24) March 30, 2021
Webデザイナーさんを含めた、フロントをコーディングしている方を対象にタスクランナーのアンケートを取ったところ、下記のような結果でした。
gulpのみか、webpackのみが圧倒的。
また何人かの方とリプをやり取りする中で、gulp+webpackのメリットがあまり感じられず、webpack一本またはgulp一本が進めやすそうという印象を受けました。
世界レベルでのビルドツールのランキング
State of JS 2020記載されている「ビルドツール」におけるランキングがこちら。
利用度(シェア率)ランキング
予想通りgulpよりwebpackの方が利用度が高いようです。

引用:https://2020.stateofjs.com/ja-JP/technologies/build-tools/
満足度ランキング
webpackが88%なのに対し、gulpは35%まで落ちてしまいました。。。

引用:https://2020.stateofjs.com/ja-JP/technologies/build-tools/
snowpackやesbuildなどビルドツールもありましたが、歴史や汎用性を考えるとwebpackが良さそうです。
また「満足度・ユーザー数」での比較結果がこちら。
結論「webpack一本に乗り換えます」
Twitterでのご意見や世界的な動向を踏まえ、わたくし上かるびはwebpack一本に乗り換えることにしました!

4月からはVue.jsも使うしね。
これからgulpを使う人のためのgulp.js(2021年版)
誰かがgulpを構築するときにヒントの1つになればいいなと思います。
gulp.jsの中身、インストール用のコマンドも載せてますので、簡単に同じ環境を作れます。
今回のgulpの導入で出来ること
- HTMLファイルの圧縮(コメント削除など)
- Sassのコンパイル(DartSass)
- CSSへ変換
- ベンダープレフィクス付与
- メディアクエリをまとめる
CSSのプロパティ順を並び替える(最近ベンダープレフィックスの記述順で不具合を見つけたのでやめました)
- JavaScriptの変換(Babel)※ES6(ES2015)の変換ができないので、一部変更しました。(2021年10月)
- 画像の圧縮
- ローカルサーバー起動
- 変更があったら自動リロード

Sassは近い将来、LibSass(現行)→DartSassへ移行されますが、本記事はDartSassに対応済みです。
gulp.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
//gulp.〇〇の処理は全部定数でまとめる const { src, dest, watch, lastRun, series, parallel } = require("gulp"); // html const htmlMin = require("gulp-htmlmin"); // Sass const sass = require("gulp-dart-sass"); const notify = require("gulp-notify"); const plumber = require("gulp-plumber"); const postCss = require("gulp-postcss"); //for autoprefixer const autoprefixer = require("autoprefixer"); const gcmq = require("gulp-group-css-media-queries"); // JavaScript const babel = require("gulp-babel"); <span class="strike">const uglify = require("gulp-uglify"); </span>const terser = require("gulp-terser");//ES6の圧縮用に追加 // 画像圧縮 const imagemin = require("gulp-imagemin"); const imageminMozjpeg = require("imagemin-mozjpeg"); const imageminPngquant = require("imagemin-pngquant"); const imageminGifsicle = require("imagemin-gifsicle"); const imageminSvgo = require("imagemin-svgo"); // ブラウザ同期 const browserSync = require("browser-sync").create(); //パス設定 const paths = { html: { src: "./src/*.html", dist: "./dist/", }, styles: { src: "./src/scss/**/*.scss", dist: "./dist/css/", map: "./map", }, scripts: { src: "./src/js/**/*.js", dist: "./dist/js/", }, images: { src: "./src/img/**/*.{jpg,jpeg,png,gif,svg}", dist: "./dist/img/", }, }; // htmlフォーマット const htmlFormat = () => { return src(paths.html.src) .pipe( plumber({ //エラーがあっても処理を止めない errorHandler: notify.onError("Error: <%= error.message %>"), }) ) .pipe( htmlMin({ //HTMLの圧縮 minifyCSS: true, //style要素とstyle属性の圧縮 minifyJS: true, //js要素とjs属性の圧縮 removeComments: true, //コメントを削除 collapseWhitespace: true, //余白を詰める collapseInlineTagWhitespace: true, //inline要素間のスペース削除(spanタグ同士の改行などを詰める preserveLineBreaks: true, //タグ間の改行を詰める <del>removeEmptyAttributes: false //空属性を削除しない</del>(デフォルトでfalseなので削除 }) ) .pipe(dest(paths.html.dist)); }; // Sassコンパイル const sassCompile = () => { return src(paths.styles.src, { sourcemaps: true, }) .pipe( plumber({ errorHandler: notify.onError("Error: <%= error.message %>"), }) ) .pipe( sass({ outputStyle: "expanded", }).on("error", sass.logError) ) .pipe( postCss([ autoprefixer({ // プロパティのインデントを整形しない cascade: false, // IE11のgrid対応 grid: "autoplace", }), ]) ) //メディアクエリをまとめる .pipe(gcmq()) .pipe( dest(paths.styles.dist, { sourcemaps: "./map", }) ) // 変更があったらリロードせずにCSSのみ更新 .pipe(browserSync.stream()) }; // JavaScriptコンパイル const jsBabel = () => { return src(paths.scripts.src) .pipe( plumber({ errorHandler: notify.onError("Error: <%= error.message %>"), }) ) .pipe( // Babel変換 babel({ presets: ["@babel/preset-env"], }) ) .pipe(dest(paths.scripts.dist)) // JS圧縮 <span class="strike">.pipe(uglify())</span> .pipe(terser()) .pipe(dest(paths.scripts.dist)); }; // 画像圧縮 const imagesCompress = () => { return src(paths.images.src, { // 更新があった場合に処理 since: lastRun(imagesCompress), }) .pipe( plumber({ errorHandler: notify.onError("Error: <%= error.message %>"), }) ) .pipe( imagemin( [ // JPG imageminMozjpeg({ quality: 80, }), // PNG imageminPngquant( [0.7, 0.8] //画質の最小,最大 ), // GIF imageminGifsicle(), // SVG imageminSvgo({ plugins: [ { removeViewbox: false, //フォトショやイラレで書きだされるviewboxを消すかどうか※表示崩れの原因になるのでfalse推奨。以降はお好みで。 }, { removeMetadata: false, //<metadata>を削除するかどうか }, { convertColors: false, //rgbをhexに変換、または#ffffffを#fffに変換するかどうか }, { removeUnknownsAndDefaults: false, //不明なコンテンツや属性を削除するかどうか→アニメーションに影響するのでfalse }, { convertShapeToPath: false, //コードが短くなる場合だけ<path>に変換するかどうか→アニメーションに影響するのでfalse }, { collapseGroups: false }, // 重複や不要な`<g>`タグを削除する→アニメーションに影響するのでfalse { cleanupIDs: false, //SVG内に<style>や<script>がなければidを削除するかどうか }, ], }), ], { //ターミナルへの情報出力非表示 verbose: true, } ) ) .pipe(dest(paths.images.dist)); }; // ローカルサーバー起動 const browserSyncFunc = (done) => { browserSync.init({ //デフォルトのconnectedのメッセージ非表示 notify: false, server: { baseDir: "./", }, startPath: "./dist/index.html", reloadOnRestart: true, }); done(); }; // ブラウザ自動リロード const browserReloadFunc = (done) => { browserSync.reload(); done(); }; // ファイル監視 const watchFiles = () => { watch(paths.html.src, series(htmlFormat, browserReloadFunc)); watch(paths.styles.src, series(sassCompile)); watch(paths.scripts.src, series(jsBabel, browserReloadFunc)); watch(paths.images.src, series(imagesCompress, browserReloadFunc)); }; // npx gulp実行処理 exports.default = series( parallel(htmlFormat, sassCompile, jsBabel, imagesCompress), parallel(watchFiles, browserSyncFunc) ); |
browserslistはpackage.jsonへ
時々gulp.jsに書いてある記事を見かけますが、非推奨なのでpackage.jsonに書きましょう。
(例)
1 2 3 4 5 6 7 8 9 10 11 |
"devDependencies": { ~(中略)~ }, "browserslist": [ <del>"last 3 version", "IE >= 10", "> 1% in JP", "not dead"</del>(デフォルトにnot deadが定義されているためエラーが出るので削除) "defaults", "safari >= 13"//あくまで例です ], |
必要なパッケージをインストールする
下記コマンドをターミナルなどで叩けば必要なものをインストールできます。(バージョンはすべて2021年3月時点の最新版で検証したところ、エラーは出ておりません。)
1 |
npm i -D @babel/core @babel/preset-env autoprefixer browser-sync browserslist gulp gulp-babel gulp-dart-sass gulp-group-css-media-queries gulp-htmlmin gulp-imagemin gulp-notify gulp-plumber gulp-postcss gulp-uglify imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo postcss |

browserslistや@babe/coreはgulp.jsに記述はないけど、gulpを動かす上では必要になってくるパッケージなので必ずインストールしましょう。

インストールする前にnpm公式サイトで確認するようにしよう!

非推奨でも普通に動くのもあるよね。

せやな。そこは自己責任で。
ディレクトリ構造
まだwebpackへ乗り換え途中ですが、完全に乗り換えることができたらまたご紹介しようかなと思います。
ご質問などありましたらコメントやTwitterでお問い合わせください。


コメント