記事執筆から1年以上が経過しています。
最新情報はnoteにて。こちらは随時更新中です!
こんにちは!フリーランスWebディレクター兼エンジニアののせっち@nosecchi01です。
以前Gulp4に関する記事を投稿しており、当ブログ内では常に上位アクセスのある人気記事となっています。
しかし、こちらはGulp v3からv4への移行期に書いた記事でして、非推奨な書き方や不要なものを含んでいました。
例えば下記。
- gulp.taskは非推奨の書き方だった。
- ソースマップが標準装備(他にもあるけど)になり、gulp-sourcemapsは不要になった。
- autoprefixerのbrowserslistはpackage.json側への記載が推奨となった。
これらに基づいてgulpfileの中身を大幅に見直しました。
結果、コード量が減って見通しがよくなったと思います。
また、下記の通り新しい要素や整理した項目があり、控えめに言ってかなり進化しました。
- webpackの導入
- 新しいgulpプラグインの導入
- 不要(使っていない)プラグインの整理
前回記事からの変更点を中心に書いていきますので、node、gulpのインストール等は割愛します。
node, gulpのインストールまでは前回記事と同様です!
ここから少し変更点の解説が入ります。
解説は要らないからコードを見たい!という方はこちらからジャンプしてくださいm(_ _)m
gulp.taskは非推奨の書き方だった。
Gulp4からgulp.task
は非推奨になりました。
<前回記事の書き方>
gulp.task('defaultTask', function(done) {
// ここにタスクを書く
done();
});
下記のような書き方になります。
function defaultTask(done) {
// ここにタスクを書く
done();
}
exports.default = defaultTask;
アロー関数などを使ってもう少しスッキリさせます。(本記事はこの書き方)
const defaultTask = done => {
// ここにタスクを書く
done();
}
exports.default = defaultTask;
非推奨とはいえ、gulp.taskでも動きますので過度に気にしなくてもいい気がしますが、gulp.taskを書かないことで、コード自体はスッキリします!
公式のリファレンスにサンプルコードがありますのでそちらも参考に!
ソースマップが標準装備になり、gulp-sourcemapsは不要になった。
Gulp4からソースマップが標準装備になり、src
,dest
の第二引数に書けばOKになりました。
const { src, dest } = require("gulp");
const sass = require('gulp-sass');
// const sourcemaps = require('gulp-sourcemaps'); 要らない!!
const compileSass = done => {
src("./src/scss/**/*.scss", { sourcemaps: true /* init */ })
.pipe(sass({outputStyle: 'expanded'}))
.pipe(dest("./dist/css", { sourcemaps: './sourcemaps' /* write */ }));
done();
}
.pipe(dest(“./dist/css”, { sourcemaps: true }));
とするとcss内に直接ソースマップが書き込まれます。
autoprefixerのbrowserslistはpackage.jsonか、.browserslistrcに書く
これはGulp4での変更点ではなく、autoprefixerの方かもですが・・・。
前回記事では、gulpfile.jsにoverrideBrowserslist
という形で記載していました。
具体的には下記の部分です。
var postcss = require('gulp-postcss'); //autoprefixerとセット
var autoprefixer = require('autoprefixer'); //ベンダープレフィックス付与
.pipe( postcss([ autoprefixer(
{
// ☆IEは11以上、Androidは5以上
// その他は最新2バージョンで必要なベンダープレフィックスを付与する
"overrideBrowserslist": ["last 2 version", "ie >= 11", "Android >= 5"],
cascade: false}
) ]) )
公式に下記のような記載があります。
The best way to provide browsers is a
.browserslistrc
file in your project root, or by adding abrowserslist
key to yourpackage.json
.(中略)
https://www.npmjs.com/package/autoprefixer
overrideBrowserslist
(array): list of queries for target browsers. Try to not use it. The best practice is to use.browserslistrc
config orbrowserslist
key inpackage.json
…(以下略)
(ざっくり翻訳)
.browserslistrcファイルを作るか、package.jsonにbroserslistキーで書いてね!overrideBrowserslist
は使わないようにしてね!
ということで、package.jsonにお引越しです。
{
// 省略
"browserslist": [
"last 1 version",
"> 1%",
"IE 11"
]
}
お引越しとともに、IE11以外は狭めの対応に変えました。
参考:そのベンダープレフィックス、いつまでつけてるの?
https://qiita.com/amamamaou/items/42197e443134478befaf
修正版の全体像を確認する
僕のgulpfile.js及びwebpack.config.jsの中身を全て公開しますので、好きなように持っていっていただいてOKです。
基本的にscssとbrowserSync、webpackでjs x babelしかやっていません。
gulpfile.js
const { src, dest, watch, series, parallel } = require("gulp");
// 直列処理(順番に処理):series(), 並列処理(順番を問わない):parallel()
const sass = require('gulp-sass');
const plumber = require('gulp-plumber');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssdeclsort = require('css-declaration-sorter');
const sassGlob = require('gulp-sass-glob'); // @importを纏めて指定
const browserSync = require('browser-sync');
const gcmq = require('gulp-group-css-media-queries'); // media queryを纏める
const mode = require('gulp-mode')({
modes: ['production', 'development'], // 本番実装時は 'gulp --production'
default: 'development',
verbose: false,
})
const webpack = require("webpack");
const webpackStream = require("webpack-stream"); // gulpでwebpackを使うために必要なプラグイン
// webpackの設定ファイルの読み込み
const webpackConfig = require("./webpack.config");
const compileSass = done => {
const postcssPlugins = [
autoprefixer({
// browserlistはpackage.jsonに記載[推奨]
cascade: false,
// grid: 'autoplace' // IE11のgrid対応('-ms-')
}),
cssdeclsort({ order: 'alphabetical' /* smacss, concentric-css */ })
]
src("./src/scss/**/*.scss", { sourcemaps: true /* init */})
.pipe(plumber())
.pipe(sassGlob())
.pipe(sass({outputStyle: 'expanded'}))
.pipe(postcss(postcssPlugins))
.pipe(mode.production(gcmq()))
.pipe(dest("./dist/css", { sourcemaps: './sourcemaps' /* write */ }));
done(); // 終了宣言
}
// ローカルサーバ起動
const buildServer = done => {
browserSync.init({
port: 8080,
notify: false,
// 静的サイト
server: {
baseDir: './',
},
// 動的サイト
// files: ['./**/*.php'],
// proxy: 'http://localsite.wp/',
})
done()
}
// ブラウザ自動リロード
const browserReload = done => {
browserSync.reload()
done()
}
// webpack
const bundleJs = () => {
// webpackStreamの第2引数にwebpackを渡す
return webpackStream(webpackConfig, webpack)
.pipe(dest("./dist/js"));
};
// ファイル監視
const watchFiles = () => {
watch('./**/*.html', browserReload)
watch('.src/scss/**/*.scss', series(compileSass, browserReload))
watch('.src/js/**/*.js', series(bundleJs, browserReload))
}
exports.sass = compileSass;
exports.default = parallel(buildServer, watchFiles);
webpack.config.js
module.exports = {
// モード値を production に設定すると最適化された状態で、
// development に設定するとソースマップ有効でJSファイルが出力される
mode: "development",
// ローカル開発用環境を立ち上げる
// 実行時にブラウザが自動的に localhost を開く
devServer: {
contentBase: "dist",
open: true // 自動的にブラウザが立ち上がる
},
// メインとなるJavaScriptファイル(エントリーポイント)
entry: `./js/main.js`,
// babel
module: {
rules: [
{
// 拡張子 .js の場合
test: /\.js$/,
use: [
{
// Babel を利用する
loader: 'babel-loader',
// Babel のオプションを指定する
options: {
presets: [
// プリセットを指定することで、ES2020 を ES5 に変換
'@babel/preset-env',
]
}
}
]
}
]
},
// ファイルの出力設定
output: {
// 出力ファイル名
filename: "bundle.js"
},
};
Webpack設定に関しては、ICS MEDIAさんのものを丸パクリで構築させていただきましたm(_ _)m
イチから構築し直す場合、以下の順番で作っていくと混乱が少ないかもです。
ということで、Webpackに関しては完全に他力本願です・・・。
ICS MEDIAさんの記事は最新版にリライトされているので安心ですし、わかりやすすぎて詰まる事なく構築できるためオススメです。(所要時間1時間弱)
gulpfile.jsの中身を解説する
ということで、gulpfile.jsの解説を中心にやっていきます。
gulp-sass
const { src, dest, watch, series, parallel } = require("gulp");
const sass = require('gulp-sass');
const plumber = require('gulp-plumber');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssdeclsort = require('css-declaration-sorter');
const sassGlob = require('gulp-sass-glob'); // @importを纏めて指定
const gcmq = require('gulp-group-css-media-queries'); // media queryを纏める
const mode = require('gulp-mode')({
modes: ['production', 'development'], // 本番実装時は 'gulp --production'
default: 'development',
verbose: false,
})
const compileSass = done => {
const postcssPlugins = [
autoprefixer({
// browserlistはpackage.jsonに記載[推奨]
cascade: false,
// grid: 'autoplace' // IE11のgrid対応('-ms-')
}),
cssdeclsort({ order: 'alphabetical' /* smacss, concentric-css */ })
]
src("./src/scss/**/*.scss", { sourcemaps: true /* init */})
.pipe(plumber())
.pipe(sassGlob())
.pipe(sass({outputStyle: 'expanded'}))
.pipe(postcss(postcssPlugins))
.pipe(mode.production(gcmq()))
.pipe(dest("./dist/css", { sourcemaps: './sourcemaps' /* write */ }));
done(); // 終了宣言
}
// ファイル監視
const watchFiles = () => {
watch('./scss/**/*.scss', series(compileSass, browserReload))
}
exports.sass = compileSass;
exports.default = parallel( /* buildServer, */ watchFiles);
変更点
- sourcemapsの記述変更(解説済み)
- メディアクエリを纏める
gulp-group-css-media-queries
を導入 - 開発モードと本番モードを分ける
gulp-mode
を導入
メディアクエリを纏める gulp-group-css-media-queries
を導入
僕はscssではクラスごとにメディアクエリを書いていくのですが、そうすると、cssにコンパイルする際に同じメディアクエリを何回も書いていることになります。
gulp-group-css-media-queries
(gcmq)を使うと、いい感じにまとめてくれるのでオススメです!
いい感じ:max-widthなら大きい順、min-widthなら小さい順に並べてくれる!
開発モードと本番モードを分ける gulp-mode
を導入
うまいやり方があるのかもしれませんが、sourcemapsとgcmqは多分両立しません。
なので、下記の対応を取ることにしています。
- 開発時はsourcemapsを使用。
- 本番環境にsourcemapsは要らないので、gcmqでメディアクエリを纏める。
gcmq
とgulp-mode
を組み合わせると、こんな感じになります。(コードは適当)
.contents {
margin: 0 auto;
max-width: 800px;
width: 100%;
@media screen and (max-width: 768px){
padding: 0 25px;
}
@media screen and (max-width: 375px){
padding: 0 15px;
}
}
h1 {
font-size: 48px;
@media screen and (max-width: 768px){
font-size: 32px;
}
@media screen and (max-width: 375px){
font-size: 24px;
}
}
開発モード(development)でコンパイル
gulp sass
.contents {
margin: 0 auto;
max-width: 800px;
width: 100%;
}
@media screen and (max-width: 768px) {
.contents {
padding: 0 25px;
}
}
@media screen and (max-width: 375px) {
.contents {
padding: 0 15px;
}
}
h1 {
font-size: 48px;
}
@media screen and (max-width: 768px) {
h1 {
font-size: 32px;
}
}
@media screen and (max-width: 375px) {
h1 {
font-size: 24px;
}
}
max-width: 768pxとmax-width: 375pxが2回出てくる。
本番(produciton)でコンパイル
gulp sass --production
.contents {
margin: 0 auto;
max-width: 800px;
width: 100%;
}
h1 {
font-size: 48px;
}
@media screen and (max-width: 768px) {
.contents {
padding: 0 25px;
}
h1 {
font-size: 32px;
}
}
@media screen and (max-width: 375px) {
.contents {
padding: 0 15px;
}
h1 {
font-size: 24px;
}
}
メディアクエリを纏めてくれた。
その他の解説事項
- autoprefixerのoptionで
grid: autoplace
にすると、gridをIE対応してくれる。 - cssdeclsortは
alphabetical
の他にsmacss
,centric-css
を指定可。(smacss
が好きな人は割といそう) - デスクトップ通知が欲しい方は
gulp-notify
を入れましょう!
gulp-notifyの例
const { src, dest } = require("gulp");
const sass = require('gulp-sass');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const compileSass = done => {
src("./src/scss/**/*.scss", { sourcemaps: true /* init */})
.pipe(plumber(({ errorHandler: notify.onError("Error: <%= error.message %>") })))
.pipe(sass({outputStyle: 'expanded'}))
.pipe(dest("./dist/css", { sourcemaps: './sourcemaps' /* write */ }));
done()
}
exports.sass = compileSass;
browser-sync
機能面は変わりませんが、若干コードがスッキリしました。
const browserSync = require('browser-sync');
// ローカルサーバ起動
const buildServer = done => {
browserSync.init({
port: 8080,
notify: false,
// 静的サイト
server: {
baseDir: './',
},
// 動的サイト
// files: ['./**/*.php'],
// proxy: 'http://localsite.wp/',
})
done()
}
// ブラウザ自動リロード
const browserReload = done => {
browserSync.reload()
done()
}
// ファイル監視
const watchFiles = () => {
watch('./**/*.html', browserReload)
watch('./src/scss/**/*.scss', series(compileSass, browserReload))
watch('./src/js/**/*.js', series(bundleJs, browserReload))
}
exports.default = parallel(buildServer, watchFiles);
notify: false
,にすると、”browser-sync connected”が出なくなります。(邪魔だから)- 静的サイトは
server
、動的サイトはproxy
を使います。
整理したもの :gulp-imagemin, gulp-ejs
使わないので削除しました。
Tips: 下記のコマンドでgulpのプラグインを消すことができます。
npm uninstall 'plugin-name'
gulp-imagemin
外部サービスで事足りているので、gulpでの圧縮はやらなくなりました。
TinyPNGが定番ですが、最近は割とSquooshもオススメです。
一枚ずつしか圧縮できないのが微妙なのですが、
- MozJPEG / WebP / OxiPNGなど圧縮できる(圧縮率高い)
- 画像サイズ(width, height)も調整できる(アスペクト比維持してくれる)
- 圧縮後の見た目を確認できる。(神)
- 圧縮はローカルで行われる。(安心)
アプリならImageOptimがオススメです。
Squoosh同様高圧縮率なフォーマットを選択できるのに加え、不要なメタ情報も削除してくれます。
もしgulp-imageminを使うなら、Gulp4からの新機能であるlastRun
を使うのは面白いかなと思います。
lastRun
は差分処理をしてくれるので、前回から比較して変更されていない画像ファイルはスキップしてくれます。
const { src, dest, lastRun, watch } = require('gulp');
const imagemin = require('gulp-imagemin');
function images() {
return src('src/images/**/*.jpg', { since: lastRun(images) })
.pipe(imagemin())
.pipe(dest('build/img/'));
}
exports.default = function() {
watch('src/images/**/*.jpg', images);
};
lastRun()
https://gulpjs.com/docs/en/api/lastrun/
それでも導入しなかったのは、watchでないと動かないからですね。
参考:gulp4 lastRun関数の挙動
https://qiita.com/masato_makino/items/da4be3ffa9c185314ae4
僕はimageminをwatchに入れないので、やっぱり要らないかなという判断です。
gulp-ejs
こちらも最近全然使っていないので削除しました。
使いたい方はこちらをどうぞ!
const ejs = require("gulp-ejs");
const rename = require("gulp-rename");
const replace = require("gulp-replace");
const compileEjs = done => {
src(["./ejs/**/*.ejs", "!" + "./ejs/**/_*.ejs"])
.pipe(plumber())
.pipe(ejs())
.pipe(rename({ extname: ".html" }))
.pipe(replace(/[\s\S]*?(<!DOCTYPE)/, "$1"))
.pipe(dest("./"));
done();
}
const watchFiles = () => {
watch('./ejs/**/*.ejs', series(compileEjs, browserReload))
}
exports.ejs = compileEjs;
exports.default = parallel(buildServer, watchFiles);
簡単なテンプレートファイルも記述しておきますm(_ _)m
<%
const metaInfo = {
title: 'トップページのタイトル',
description: 'トップページのディスクリプション',
path: './'
}
%>
<%- include(metaInfo.path + 'common/_header', {metaInfo: metaInfo} ) %>
<main>
<h1>Hello ejs</h1>
<p>トップページのテキスト</p>
</main>
<%- include(metaInfo.path + 'common/_footer', {metaInfo: metaInfo} ) %>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= metaInfo.title %></title>
<meta name="description" content="<%= metaInfo.description %>">
<link rel="stylesheet" href="<%= metaInfo.path %>/dist/css/style.css">
</head>
<body>
<script src="<%= metaInfo.path %>/dist/js/bundle.js"></script>
</body>
</html>
参考記事
下記の記事を参考にしました。ありがとうございますm(_ _)m
Gulp公式
https://gulpjs.com/
gulp v3からv4への記述法移行
https://qiita.com/soraxism/items/6146155b0255ef3f1e99
Gulp4がリリースされたのでgulpfile.jsをアップデートした
https://qiita.com/hibikikudo/items/493fbfbbea183c94b38b
→変更点が非常にわかりやすい記事です。
Gulp4が正式リリースされたので、gulpfile.jsの中身を見直してみましたgulp4+pug+scss
https://qiita.com/sam_sam/items/45db2c948e47e82a5386
gulp v4でgulp.taskを使わないで記載してみる
https://qiita.com/miwashutaro0611/items/24f156868350503d4875
gulp4再入門 gulpfileの分割とnodeモジュールの利用
https://qiita.com/masato_makino/items/ad11058e1a8a009abbdf
→gulpfile.jsは分割管理もできるようです
Javascriptの知識があると環境構築が捗る
gulpfile.jsは当然javascriptですから、バニラjsの知識があると環境構築はグンと捗ります。
今回のGulpアップデートにあたり、「バニラjsの勉強しておいて本当によかった・・・」と強く感じました。
Webサービスでの勉強なら断然ドットインストールがオススメです。
講座数がめちゃくちゃ多いのが特徴です!
また、書籍でしたら実はKindle Unlimitedが割とオススメです。
技術書は基本的に高いので、1〜2ヶ月に1冊程度のペースでも、月額980円のKindle Unlimitedの方がお得です!
30日間無料なので、「微妙だな・・・」と思ったら止めればOKです。
とはいえ、Kindleにも結構技術書はあります。 → Kindle Unlimitedの技術書(一部)