Gulpとは?

放課後のプログラミング部。部室でウェブサイト制作の課題に取り組んでいたOZくんは、ため息をつきながらパソコンの画面を見つめていました。

プロ太
プロ太

OZくん、どうしたんですか?なんだかため息が出るほど悩んでいるようですが、、

OZ
OZ

あ、プロ太先生…実は、ウェブサイトの制作をしているんですけど、毎回の更新作業が大変で…

具体的にはどんな作業が大変なんですか?

はい、CSSファイルの圧縮とか、画像ファイルの圧縮とか…毎回手作業でやるのが面倒で。しかも、作業を忘れたりミスしたりすることもあって…

なるほど!そんなOZくんにぴったりのツールがありますよ。
その名も『Gulp(ガルプ)』!

ガルプ…?なんですか、それ?

Gulpとは?

Gulpは、タスクランナーと呼ばれるツールなんです。タスクランナーって聞くと難しそうですが、要するに『面倒な作業を自動化してくれる便利な助手さん』だと思ってくれればいいですよ。

助手さん、、、ですか?

そうです。例えば、学校の掃除当番を想像してみてください。毎日、『ゴミ箱を空にする→床を掃く→雑巾がけ』って決まった手順でやりますよね?

はい、そうですね。

Gulpは、そういう『決まった手順の作業』を、君の代わりに自動でやってくれるです。しかも、人間より正確で、疲れ知らずですからね!

へぇ!それは便利そうですね!

Gulpでできること

じゃあ、Gulpでできることを具体的に見ていきましょう。主な機能をいくつか紹介しますね。

  1. ファイルの圧縮(Minification)
    • CSSやJavaScriptファイルを圧縮して、サイズを小さくできる
  2. ファイルの結合(Concatenation)
    • 複数のファイルを1つにまとめられる
  3. ブラウザの自動リロード(Browser Sync)
    • ファイルを保存すると自動でブラウザをリフレッシュ
  4. Sassのコンパイル
    • SassをCSSに変換
  5. 画像の最適化
    • 画像ファイルを圧縮して軽量化

すごい!!こんなにたくさんのことができるんですね!!

Gulpの導入

じゃあ、実際にGulpを使えるようにするための準備をしていきましょう。

1.Node.jsのインストール

まずはNode.jsというソフトをインストールする必要があります。これは、お店でガルプ君(Gulp)を雇う前に、お店(パソコン)の経営許可を取るようなものですね。

  1. Node.jsの公式サイトにアクセス
    https://nodejs.org/
  2. LTS(Long Term Support)版をダウンロード
    →「LTS」と書かれているボタンをクリック
  3. ダウンロードしたインストーラーを実行
  4. 画面の指示に従ってインストール完了

インストールができたら、ターミナル(Windowsならコマンドプロンプト)で以下のコマンドを実行して、ちゃんとNode.jsがインストールされたか確認してみましょう。

# バージョンの確認
node -v
npm -v

両方のコマンドでバージョン番号が表示されればOKということですね!

2.Gulpのインストール

次は、いよいよ、Gulpをインストールしましょう!

# グローバルにGulpをインストール
npm install -g gulp-cli

# プロジェクトフォルダで以下のコマンドを実行
npm init -y
npm install --save-dev gulp

なんだか難しそうなコマンドがいっぱい出てきましたね…

大丈夫!これは最初の1回だけですから。お店の開店準備みたいなものですよ。念のため、それぞれのコマンドの意味を説明しておきますね。

Step 1: Gulpのコマンドラインツールをグローバルにインストール

npm install -g gulp-cli
  • npm install: パッケージをインストールするコマンド
  • -g: グローバルフラグ。インストールしたPCのどこからでもgulpコマンドを使えるようにする
    →例えば、スマホでアプリを入れて、どこでも使えるようにするイメージです
    →このコマンドは、一度実行して、自分のPCにインストールが完了すれば2回目以降は不必要
  • gulp-cli: Gulpのコマンドラインインターフェース。gulpコマンドを使うために必要なツール

へぇ〜!グローバルでインストールしたら、どこでも使えるようになるんですね!

Step 2: プロジェクトの初期化

npm init -y

このコマンドは、プロジェクトの設定ファイルを作成しています。

  • npm init: プロジェクトの初期化コマンド
  • -y: 全ての設定をデフォルトで「yes」にするオプション
    →npm init と入力し、設定を入力してもOK!
  • 実行すると以下のような、package.jsonというファイルが作成される
{
  "name": "プロジェクト名",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

ちなみに、このコマンドで作られるpackage.jsonは、とても重要で便利なファイルなんです!

このファイルが便利なんですか??

そうなんです。これは単なる設定ファイルじゃなくて、『プロジェクトの設計図』みたいなものなんです。例えば、先程のコマンドで出来上がったpackage.jsonのファイルの中身を以下のように書き換えてみてください。

{
  "name": "gulp_test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    "build": "gulp build"
    "watch": "gulp watch"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-changed": "^4.0.3",
    "gulp-clean-css": "^4.3.0",
    "gulp-imagemin": "^7.1.0",
    "gulp-dart-sass": "^1.0.2",
    "browserify": "^17.0.0",
    "vinyl-source-stream": "^2.0.0",
    "browser-sync": "^2.27.7"
  }
}

これは、私が開発で使っているpackage.jsonファイルの中身です。これがあれば、他の人も私と全く同じ開発環境が簡単に作れるんです!

開発環境の共有手順
  1. 共有する側がすること
    • package.jsonを含むプロジェクトファイルを共有
    • (注:node_modulesフォルダは共有不要!)
  2. 受け取った側がすること
# プロジェクトフォルダで以下のコマンドを実行するだけ!
npm install

こんな風に同じような開発環境を作りたい相手に、このpackage.jsonを渡して、渡された側は上のコマンドを入力するだけで、簡単に同じ開発環境が作れちゃいます!

なるほど!じゃあ、チームで開発するときも便利ですね!

その通り!特に以下のような場面で重宝します!!

package.jsonが特に役立つシーン
  1. チーム開発時
    • 新しいメンバーが参加したとき
    • 全員が同じバージョンのツールを使える
  2. 複数のパソコンで開発するとき
    • 会社と自宅で開発する場合
    • 新しいパソコンでの環境構築
  3. プロジェクトの引き継ぎ時
    • 必要な開発環境の情報が全て残っている
    • 引き継ぎ作業が格段に楽になる

すごい!!便利さが分かりました!!ありがとうございます!!

基本的なgulpfile.jsの書き方

次は、Gulpに『どんな作業をしてほしいか』を伝えるための指示書を作っていきます。『gulpfile.js』というファイルを作って、以下のような内容を書き込みます。

// パッケージの読み込み
const gulp = require("gulp");
const dartSass = require("gulp-dart-sass");
const cleanCSS = require("gulp-clean-css");
const changed = require("gulp-changed");
const imagemin = require("gulp-imagemin");
const browserify = require("browserify");
const source = require("vinyl-source-stream");
const browserSync = require("browser-sync").create();

// ファイルパスの設定
const paths = {
  styles: {
    src: "src/scss/**/*.scss",
    dest: "dist/css",
  },
  images: {
    src: "src/images/**/*",
    dest: "dist/images",
  },
  scripts: {
    entries: ["src/js/main.js"],
    dest: "dist/js",
  },
  php: {
    src: "./*.php",
    dest: "dist/",
  },
};

// Sassのコンパイル
function styles() {
  return gulp
    .src(paths.styles.src)
    .pipe(changed(paths.styles.dest))
    .pipe(
      dartSass({
        outputStyle: "compressed", // CSSを圧縮
      }).on("error", dartSass.logError)
    )
    .pipe(cleanCSS())
    .pipe(gulp.dest(paths.styles.dest))
    .pipe(browserSync.stream()); // CSSの変更時にブラウザを更新
}

// 画像の最適化
function images() {
  return gulp
    .src(paths.images.src)
    .pipe(changed(paths.images.dest))
    .pipe(
      imagemin([
        imagemin.gifsicle({ interlaced: true }),
        imagemin.mozjpeg({ quality: 75, progressive: true }),
        imagemin.optipng({ optimizationLevel: 5 }),
        imagemin.svgo({
          plugins: [{ removeViewBox: true }, { cleanupIDs: false }],
        }),
      ])
    )
    .pipe(gulp.dest(paths.images.dest));
}

// JavaScriptのバンドル
function scripts() {
  return browserify({
    entries: paths.scripts.entries,
    debug: true,
  })
    .bundle()
    .pipe(source("bundle.js"))
    .pipe(gulp.dest(paths.scripts.dest))
    .pipe(browserSync.stream()); // JavaScriptの変更時にブラウザを更新
}

// PHPファイルのコピー
function copyPhp() {
  return gulp.src(paths.php.src).pipe(gulp.dest(paths.php.dest)); 
}

// BrowserSyncを起動するタスク
function serve() {
  browserSync.init({
    proxy: "localhost:8888/◯◯◯◯", 
  // MAMPのプロキシ設定(◯◯◯◯の部分はhtcocs内においたファイル名)
  });

  gulp.watch(paths.styles.src, styles);
  gulp.watch(paths.images.src, images);
  gulp.watch("src/js/**/*.js", scripts);
  gulp.watch(paths.php.src, copyPhp).on("change", browserSync.reload);
}

// ビルドタスク
const build = gulp.parallel(styles, images, scripts, copyPhp);

// エクスポート
exports.styles = styles;
exports.images = images;
exports.scripts = scripts;
exports.copyPhp = copyPhp;
exports.build = build;
exports.watch = serve;
exports.default = gulp.series(build, serve);

うわぁ…これは何をしているんですか?

順番に説明していきますね!

1. パッケージの読み込みとパスの設定

// パッケージの読み込み
const gulp = require("gulp");
const dartSass = require("gulp-dart-sass");
const cleanCSS = require("gulp-clean-css");
const changed = require("gulp-changed");
const imagemin = require("gulp-imagemin");
const browserify = require("browserify");
const source = require("vinyl-source-stream");
const browserSync = require("browser-sync").create();

// ファイルパスの設定
const paths = {
    styles: {
        src: "src/scss/**/*.scss",
        dest: "dist/css",
    },
    images: {
        src: "src/images/**/*",
        dest: "dist/images",
    },
    scripts: {
        entries: ["src/js/main.js"],
        dest: "dist/js",
    },
    php: {
        src: "./*.php",
        dest: "dist/",
    },
};

最初の部分は、必要な道具を用意している部分です。お料理で言えば、包丁やまな板を準備するようなものですね。

  • gulp-dart-sass: Sassをコンパイルするための道具
  • gulp-clean-css: CSSを圧縮する道具
  • gulp-changed: 変更があったファイルだけを処理する道具
  • gulp-imagemin: 画像を最適化する道具
  • browserify: JavaScriptファイルをまとめる道具
  • browser-sync: ブラウザを自動更新する道具

Gulpを使うときのファイルの配置って、どうすればいいんですか?

いい質問ですね!では、プロジェクトの構造を見てみましょう。基本的にこんな感じになります。

プロジェクトフォルダ/
├── src/                  # 開発用のソースファイル
│   ├── scss/            # Sassファイル
│   │   ├── style.scss
│   │   └── _variables.scss
│   ├── images/          # 画像ファイル
│   │   ├── hero.jpg
│   │   └── logo.png
│   └── js/              # JavaScriptファイル
│       └── main.js
├── dist/                # コンパイル後のファイル
│   ├── css/            # コンパイル後のCSSファイル
│   ├── images/         # 最適化後の画像ファイル
│   └── js/             # バンドル後のJavaScriptファイル
├── package.json        # プロジェクトの設定ファイル
├── gulpfile.js         # Gulpの設定ファイル
└── index.php           

おお!フォルダがきれいに分かれているんですね!!

srcは「素材置き場」、distは「完成品置き場」って考えるといいですよ。

2. Sassのコンパイルタスク

function styles() {
    return gulp
        .src(paths.styles.src)
        .pipe(changed(paths.styles.dest))
        .pipe(
            dartSass({
                outputStyle: "compressed", // CSSを圧縮
            }).on("error", dartSass.logError)
        )
        .pipe(cleanCSS())
        .pipe(gulp.dest(paths.styles.dest))
        .pipe(browserSync.stream());
}

このpipeって何度も出てきますけど…なんですか?

ああ、これは「作業の順番」を表しています。例えると、お弁当工場の生産ラインみたいなもので、以下のような手順で処理をしています。

  1. 材料を用意する(.src)
  2. 変更チェック(changed)
  3. Sassをコンパイル(dartSass)
  4. CSSを圧縮(cleanCSS)
  5. 保存(gulp.dest)
  6. ブラウザ更新(browserSync.stream) という流れで処理しています。

3. 画像の最適化処理

function images() {
    return gulp
        .src(paths.images.src)
        .pipe(changed(paths.images.dest))
        .pipe(
            imagemin([
                imagemin.gifsicle({ interlaced: true }),
                imagemin.mozjpeg({ quality: 75, progressive: true }),
                imagemin.optipng({ optimizationLevel: 5 }),
                imagemin.svgo({
                    plugins: [{ removeViewBox: true }, { cleanupIDs: false }],
                }),
            ])
        )
        .pipe(gulp.dest(paths.images.dest));
}

ここでは、画像の最適化をしていて、具体的に言うと、

  • JPEGの画質を75%に設定(quality: 75)
  • PNGの圧縮レベルを5に設定(optimizationLevel: 5)
  • GIFをインターレース形式に
    というように、それぞれの画像形式に最適な設定で圧縮しています。

4. JavaScriptのバンドル処理

function scripts() {
    return browserify({
        entries: paths.scripts.entries,
        debug: true,
    })
        .bundle()
        .pipe(source("bundle.js"))
        .pipe(gulp.dest(paths.scripts.dest))
        .pipe(browserSync.stream());
}

これは複数のJavaScriptファイルを1つにまとめる処理ですね。例えると、バラバラのレシピをまとめて1冊の料理本にするようなものです。

5.開発サーバーとファイル監視

function serve() {
    browserSync.init({
        proxy: "localhost:8888/◯◯◯◯",
    });
    
    gulp.watch(paths.styles.src, styles);
    gulp.watch(paths.images.src, images);
    gulp.watch("src/js/**/*.js", scripts);
    gulp.watch(paths.php.src, copyPhp).on("change", browserSync.reload);
}

これは「自動監視システム」みたいなもので、

  1. ローカルサーバーを立ち上げて
  2. ファイルの変更を監視して
  3. 変更があったら自動で必要な処理を実行して
  4. ブラウザを更新する という一連の作業を自動でやってくれるんだ。

6. タスクのエクスポートと実行

const build = gulp.parallel(styles, images, scripts, copyPhp);
exports.styles = styles;
exports.images = images;
exports.scripts = scripts;
exports.copyPhp = copyPhp;
exports.build = build;
exports.watch = serve;
exports.default = gulp.series(build, serve);

これは「使える機能の登録」みたいなものだね。例えば、

  • npm run build: 全ての処理を一度に実行
  • npm run watch: 監視モードを開始 というように使えるようになります。

Gulpの使い方

なるほど!!でも、実際にはどうやって使うんですか?

コマンドラインで以下のように入力するだけでOKです!

1.デフォルトタスクの実行:

gulp

このコマンドを実行すると、さっき書いた指示書(gulpfile.js)の最後のの方で書いたexports.defaultで設定したタスクを実行します。今回の場合は、buildserveが順番に実行されますね。

えぇっ!?たったそれだけですか!?簡単ですね!

2.特定のタスクの実行:

gulp styles    # Sassのコンパイルだけを実行
gulp images    # 画像の最適化だけを実行
gulp scripts   # JavaScriptのバンドルだけを実行
gulp build     # 全てのビルドタスクを実行
gulp watch     # ファイル監視を開始

特定のタスクだけ実行したい場合は、上記のようなコマンドの入力で大丈夫です。

あれ?先生、さっきはnpm run buildって言ってませんでした?

実は両方使えるんです。その違いを説明しますね。

npmコマンドとgulpコマンドの違い

1.gulpコマンドを使う場合:

gulp build
  • Gulpをグローバルにインストールしている必要がある
  • シンプルで直接的
  • プロジェクトによってコマンドが統一されない可能性がある

2.npm run コマンドを使う場合:

npm run build
  • package.jsonのscriptsに定義が必要(以下の部分参照)
  • プロジェクト内のGulpを使用するので安全
  • プロジェクト間でコマンドを統一しやすい
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    "build": "gulp build"
    "watch": "gulp watch"
  },

まとめ

どうですか?Gulpについて少しわかってきましたか?

はい!最初は難しそうに見えましたが、要するに:

  1. 面倒な作業を自動化してくれる
  2. ミスなく正確に作業をしてくれる
  3. 時間を大幅に節約できる  ということですよね?

その通りです!特に大きなプロジェクトになればなるほど、Gulpの便利さは実感できるはずですよ!

早速、次のプロジェクトで使ってみます!ありがとうございました、先生!

このように、Gulpは現代のWeb開発において非常に重要なツールの1つとなっています。確かに最初の設定は少し手間がかかりますが、一度設定してしまえば、その後の開発効率は大きく向上します。

特に以下のような場合には、Gulpの導入を検討してみることをおすすめします:

  • 複数のSassファイルやJavaScriptファイルを扱うプロジェクト
  • チームで開発を行うプロジェクト
  • 定期的なファイル更新が必要なプロジェクト
  • 作業の自動化によって品質向上を図りたい場合

Gulpを使いこなすことで、より効率的で品質の高いWeb開発が可能になるはずです。