1. 一定要記得 return gulp.src stream
剛開始學習的時候,網路上的教材有兩種寫法。
第一種:無 return
gulp.task("taskName", function() { gulp.src("檔案")... }
第二種:有 return
gulp.task("taskName", function() { // 加了 return 在 gulp src 前面 return gulp.src("檔案")... }
第一種跟第二種的差別在於,有 return 的我們會知道 tasks 已經結束的時機,而前面沒加 return 的我們不會知道。
gulp 很聰明。在還沒到 concurrency 上限前,預設會同時跑所有需要的 tasks,並且不等待任何返回值。
(簡單來說。By default, gulp task is asynchronous task)
這時,當我們需要有順序的跑 tasks 時,就會因為完成順序不如預期出現錯誤。
範例,我們希望在跑 minify css 之前,先做 scss 編譯。
var sass = require('gulp-ruby-sass'); var minifyCSS = require('gulp-minify-css'); gulp.task('scss', function() { // syntax change gulp-ruby-sass starting from 1.0.0-alpha sass('./scss', { style: 'expanded' }) .pipe(gulp.dest('./css/')); }); // 期望 scss 跑完在跑 minify css, // ['scss'] 是指定 dependency,styles task 會在 scss 跑完後再跑 gulp.task('styles', ['scss'], function () { gulp.src('./css/**/*.css')terminal 結果:.pipe(minifyCSS()) .pipe(gulp.dest('./dist/css/'));});
myBook:gulp-test yvonne$ gulp styles [21:30:25] Using gulpfile ~/work/gulp-test/gulpfile.js [21:30:25] Starting 'scss'... [21:30:25] Finished 'scss' after 5.99 ms // 跑完 scss... [21:30:25] Starting 'styles'... // 開始跑 styles... [21:30:25] Finished 'styles' after 4.11 ms [21:30:26] directory ./ [21:30:26] write ./main.css // 這裏才是 scss 真正編譯完成
雖然結果顯示 styles 是等 scss 跑完後才跑的。
可是後面的 log 卻寫說編譯的 css 是等全部結束後才寫入,結果根本非預期想的那樣。
解法非常簡單,只要加 return 回傳 stream 提示 tasks 結束即可。
var sass = require('gulp-ruby-sass'); var minifyCSS = require('gulp-minify-css'); gulp.task('scss', function() { return sass('./scss', { style: 'expanded' }) .pipe(gulp.dest('./css/')); }); // 期望 scss 跑完在跑 minify css, // ['scss'] 是指定 dependency,styles task 會在 scss 跑完後再跑 gulp.task('styles', ['scss'], function () { return gulp.src('./css/**/*.css') .pipe(minifyCSS()) .pipe(gulp.dest('./dist/css/')); });
myBook:gulp-test yvonne$ gulp styles [21:39:02] Using gulpfile ~/work/gulp-test/gulpfile.js [21:39:02] Starting 'scss'... [21:39:02] directory ./ [21:39:02] write ./main.css // scss 編譯完成! [21:39:03] Finished 'scss' after 249 ms [21:39:03] Starting 'styles'... // styles 才開始跑 [21:39:03] Finished 'styles' after 24 ms
為了避免類似的問題再發生,建議是 return 所有的 gulp stream。
只要沒有特定指定 dependency 的情況下, gulp tasks 都會是採非同步的方式跑。一樣效能很好!
ref:
http://stackoverflow.com/questions/21699146/gulp-js-task-return-on-src
2. 使用外部的 config 管理變數(like grunt)
在 gulp.js 的 github recipes 上有一則如何使用 json 管理 gulp 變數,維持 tasks DRY(Don't Repeat Yourself)。教學用法是把變數寫入在外部的 json 檔案,然後 tasks 可呼叫相同變數。開發者只需要維護 json 裡的變數即可,而不用維護分佈在不同 tasks 的變數。
雖然用 json 維護很方便,可是我個人喜歡用 node 產生 config module,讓變數可有繼承關係。
新增 gulp.config.js
module.exports = function () { var client = 'root/', dist = client + 'dist/'; return { // root client: client, scss: { files: client + '/scss/**/*.scss', css: { distPath: dist + 'css/', name: 'main.css' } } }; };
在 gulpfile.js 記得把 config.js require 進來
var gulp = require('gulp'), // 需要啟用 config module 的 function config = require('./gulp.config')(); // print 'root/' console.log('client path:', config.client);
3. del 取代 rimraf
刪除檔案在 gulp task 是很常見的動作。最常使用方式是當我們需要重新編譯檔案時,先把原先編譯完的舊檔案刪除,避免發生未知的錯誤...
(例如,其實重新編譯的檔案沒有成功,可是因為舊的檔案存在,所以一直以為有成功。然後就 debug 一陣子...)
(或是例如有些套件有 cache 的問題...)
常見用法如下:(scripts 前先清除之前的 js 檔案 )
var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var del = require('del'); // pass callback argument 非常重要!! gulp.task('clean:js', function (cb) { // 刪除在 js folder 下面全部的檔案 del(['dist/js/**.*'], cb); }); gulp.task('scripts', ['clean:js'], function() { gulp.src('./js/**/*.js') .pipe(uglify()) .pipe(concat('all.min.js')) .pipe(gulp.dest('./dist/js/')) });
del 用法很簡單,就是把想要刪除的 path array 放在第一個 argument 即可。(支援 globbing patterns)
然後別忘了要 pass callback,原因跟上面介紹的第一點『 return steam 』原理一樣。當有 dependency 的時候,我們需要讓 task 知道刪除結束的時間點。del 套件在刪除檔案時會呼叫 callback,通知 task dependency 已經結束了~ 這樣才不會刪除跟編譯同時進行喔!