gulp-watch + gulp-notify + browser-sync で開発効率アップを図る - Gulp で作る Web フロントエンド開発環境 #2

前回の記事では Gulp を使って SCSS (Sass) をコンパイルしたり Bourbon フレームワークを使えるようにするタスクを作成しました。今回はそれを元に以下のパッケージを導入して開発効率のアップを図りたいと思います。

  • gulp-watch
  • gulp-plumber
  • gulp-notify
  • browser-sync

サンプルコードはこちらからどうぞ。

gulp.watch でファイルの更新を監視してタスクを実行

SCSS ファイルを編集して保存したタイミングでコンパイルタスクを実行するようにします。といっても Grunt と違って Gulp にはgulp.watch()という標準で監視機能を持っているので、すぐに試すことが出来ます。

gulpfile.jsに以下のコードを追記します。

gulp.task('watch', () => {
    return gulp.watch(['./src/scss/**/*.scss'], ['sass']);
});

watchというタスクを追加し、その中で gulp.watch()を呼び出します。gulp.watch(glob[, opts, cb])という構造になっており、glob には監視対象となるファイルを指定します。配列形式で複数指定することが出来ます。opts に監視対象が更新されたタイミングで実行したいタスクを指定します。こちらも配列形式で複数指定することが出来ます。

実行してみます。

$ gulp watch
[00:51:21] Using gulpfile ~/Documents/sandbox/try_gulp/try_browsersync/gulpfile.js
[00:51:21] Starting 'watch'...
[00:51:21] Finished 'watch' after 9.69 ms

タスクが終了せずに Gulp が監視状態に入っています。この状態で ./src/css/modules/_card.scssを編集してみましょう。

[00:53:28] Starting 'sass'...
[00:53:28] Finished 'sass' after 72 ms

タスクが自動で実行されました。これでファイル更新の度に手動で Gulp を実行する手間が省けます。監視状態を終了するには control + cを押します。

gulp-watch でファイルの追加も監視対象に含める

残念ながら今紹介した gulp.watch() は、新規で追加されたファイルをそのまま監視することが出来ません。つまり新規でファイルを追加したら、監視を再起動する必要があるというわけです。そこでファイルの新規追加も監視できるように gulp-watchを使います。

パッケージをインストールします。


監視タスクを定義しなおします。

var watch = require('gulp-watch');
gulp.task('watch', () => {
    return watch(['./src/scss/**'], () => {
        return gulp.start(['sass']);
    });
});

これで実行してみます。

$ gulp watch
[01:15:33] Using gulpfile ~/Documents/sandbox/try_gulp/try_browsersync/gulpfile.js
[01:15:33] Starting 'watch'...

試しに SCSS ファイルを新規に追加してみます。

//
// Button
// --------------------------------------------------
// Block
// --------------------
.button {
  overflow: hidden;
  padding: 10px;
  cursor: pointer;
  border: 1px solid #DDDDDD;
  border-radius: .4em;
  background: #EEEEEE;
  text-shadow: 0 1px rgba(255,255,255,.3);
  @include transition(.1s);
  &:hover,
  &:focus,
  &:active {
    box-shadow: 0 2px 1px rgba(0,0,0,.1);
  }
}
@import "bourbon";
@import "variables";
@import "scaffolding";
@import "modules/button";
@import "modules/card";

新規ファイルを追加して更に編集しても監視は正常に行われ、タスクが実行されているのが分かります。

[01:15:46] Starting 'sass'...
[01:15:47] Finished 'sass' after 78 ms
[01:16:27] Starting 'sass'...
[01:16:27] Finished 'sass' after 52 ms
[01:16:50] Starting 'sass'...
[01:16:50] Finished 'sass' after 46 ms

これでファイル監視がより強力になりましたが、監視中に何らかのコンパイルエラーが発生するとその時点で監視が終了してしまいます。ちょっとしたタイポやファイルの保存の順序などで発生する一時的なエラーのせいでいちいち監視を終了されては面倒です。

gulp-plumber で監視中のエラーによる強制停止を阻止する

パッケージをインストールします。


sass タスクを修正します。

var plumber = require('gulp-plumber');
gulp.task('sass', () =>{
    return gulp.src('./src/scss/**/*.scss')
        .pipe(plumber(function(error) {
            return this.emit('end');
        }))
        .pipe(sourcemaps.init())
        .pipe(sass({
            includePaths: bourbon.includePaths
        }))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('./dest/assets/css'));
});

タスクを実行してみます。

$ gulp watch
[01:59:45] Using gulpfile ~/Documents/sandbox/try_gulp/try_browsersync/gulpfile.js
[01:59:45] Starting 'watch'...

SCSS ファイルを壊して意図的にエラーを出してみます。

[01:59:50] Starting 'sass'...
[01:59:50] Finished 'sass' after 63 ms

特に変わったこともなく、監視は継続されているようです。SCSSファイルを修正すると、何事もなかったかのように更新を検知してタスクが実行されます。

監視の強制終了が阻止されたのは良いことですが、エラーメッセージが表示されません。原因はplumber()に渡したオプションの関数内で実行している@emit('end')にあります。公式ページによると plumber()には何のオプションも渡す必要はないのですが、それだと監視の強制終了は阻止できても次の更新が検知出来ずにいます。この辺りに関する Issue が挙がっていますが、まだクローズされていませんね・・・。

これについては、次で紹介する gulp-notify を導入することで(ひとまず)解決します。

gulp-notify でデスクトップ通知を表示する

Gulp 上で発生したエラーをデスクトップ通知することが出来ます。

パッケージをインストールします。


sass タスクを修正します。

var notify = require('gulp-notify');
gulp.task('sass', () => {
    return gulp.src('./src/scss/**/*.scss')
        .pipe(plumber({
            errorHandler: notify.onError('<%= error.message %>')
        }))
        .pipe(sourcemaps.init())
        .pipe(sass({
            includePaths: bourbon.includePaths
        }))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('./dest/assets/css'));
});

タスクを実行し、SCSS ファイルを壊して意図的にエラーを出してみます。

gulp-notify

デスクトップに通知が表示されました。

[02:48:02] Starting 'sass'...
[02:48:02] gulp-notify: [Error running Gulp] unbound variable $card-padding-default
[02:48:02] Finished 'sass' after 296 ms

先ほどの plumber の件といい、この辺りの理解がまだ不足しています。

browser-sync で複数のブラウザ間での操作を同期する

その名の通り複数のデバイス・ブラウザ間でのスクロールやクリックといったマウス操作からページ遷移などを同期してくれるパッケージです。しかもローカルサーバーの立ち上げやライブリロード機能も備えているという優れものです。

パッケージをインストールします。


タスクを定義します。

var browserSync = require('browser-sync');
gulp.task('browser-sync', () => {
    return browserSync.init(null, {
        server: './dest'
    });
});
gulp.task('default', ['sass', 'browser-sync', 'watch']);
server ローカルサーバー立ち上げ時のルートディレクトリを指定

タスクが増えてきたのでdefaultというタスクを定義し、これまでのタスクを順番に実行するようにしました。

実行してみます。タスク名を指定しないと default が実行されます。

$ gulp
[10:38:46] Using gulpfile ~/Documents/sandbox/try_gulp/try_browsersync/gulpfile.js
[10:38:46] Starting 'sass'...
[10:38:46] Starting 'browser-sync'...
[10:38:46] Finished 'browser-sync' after 58 ms
[10:38:46] Starting 'watch'...
[10:38:46] Finished 'sass' after 154 ms
[BS] Access URLs:
 -------------------------------------
       Local: http://localhost:3000
    External: http://192.168.2.54:3000
 -------------------------------------
          UI: http://localhost:3001
 UI External: http://192.168.2.54:3001
 -------------------------------------
[BS] Serving files from: ./dest

サーバーが立ち上がり、自動でブラウザがhttp://localhost:3000を開くはずです。複数のブラウザを立ち上げて操作してみると同期がとれているのが分かります。

demo-browsersync

LiveReload を試してみる

SCSS ファイルを更新したら自動でブラウザがリロードするようにしてみます。

gulp.task('sass', () => {
    return gulp.src('./src/scss/**/*.scss')
        .pipe(plumber({
            errorHandler: notify.onError('<%= error.message %>')
        }))
        .pipe(sourcemaps.init())
        .pipe(sass({
            includePaths: bourbon.includePaths
        }))
        .pipe(sourcemaps.write())
        .pipe(gulp.dest('./dest/assets/css'))
        .pipe(browserSync.reload({
            stream: true
        }));
});
gulp.task('browser-sync', () => {
    return browserSync.init(null, {
        server: './dest',
        reloadDelay: 2000
    });
});

sassタスク内に browser sync のリロード処理を追加しました。また、リロード実行を2秒遅らせる指定も追加しています。これでタスクを実行し、SCSS ファイルを更新すれば表示している全てのブラウザにリロードが走ります。

この他にも BrowserSync には様々な機能が搭載されています。とてもここでは紹介しきれませんが、公式ドキュメントを参考にして役立ちそうなのを片っ端から試してみるとよいでしょう。