gulp-watch + gulp-notify + browser-sync で開発効率アップを図る - Gulp で作る Web フロントエンド開発環境 #2
wakamsha
前回の記事では 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 ファイルを壊して意図的にエラーを出してみます。
[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
を開くはずです。複数のブラウザを立ち上げて操作してみると同期がとれているのが分かります。
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 には様々な機能が搭載されています。とてもここでは紹介しきれませんが、公式ドキュメントを参考にして役立ちそうなのを片っ端から試してみるとよいでしょう。