背景
Gulp 默認將所有任務和步驟異步化運行。顯而易見,Gulp 在效率上是有明顯的提升的。但是如果需要同步執行任務序列時,比如我們進行資源打包之前,應該先清掉鏡像文件目錄dist中的資源。如果按照默認的配置去配置任務,就會出現問題。本文針對gulp同步任務中容易出錯的地方做了總結及給出了可行的方案。
gulp異步任務
因為任務是異步運行的,Gulp 便默認將并行運行所有任務;任務中的步驟也是異步的,因此各個步驟也是并行的。比如在下面的任務里,兩個操作將會并行運行:
var gulp = require('gulp');
var zip = require('gulp-zip');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
gulp.task('less:dist', function () {
//編譯less然后壓縮css并拷貝到dist
gulp.src(['src/**/*.less'])
.pipe(less())
.pipe(minifycss())
.pipe(rename(function (path) {
path.extname = ".css"
}))
.pipe(gulp.dest('dist'));
//壓縮css并拷貝到dist
gulp.src(['src/**/*.css'])
.pipe(minifycss())
.pipe(gulp.dest('dist'));
});
再比如將build任務依賴clean,copy-common,package三個任務,執行build任務時,三個依賴的任務是并行進行的:
var gulp = require('gulp');
var zip = require('gulp-zip');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
gulp.task("copy-common", function () {
gulp.src(['client/**/**','!client/dev.html','!client/index.hbs','build/**/**'])
.pipe(rename(function (path) {
path.dirname += '';
}))
.pipe(gulp.dest("./dist/pages/currency"))
})
//清空dist目錄
gulp.task("clean",function(){
console.log('清空 dist 目錄下的資源')
gulp.src('dist/*', {
read: false
})
.pipe(clean({
force: true
}));
})
//生成生產war包
gulp.task("package", function () {
gulp.src(['dist/**']).pipe(zip('dist.war')).pipe(gulp.dest('./'));
console.info('package ok!');
});
gulp.task('build',['clean','copy-common1','package'])
如何將gulp任務序列同步執行
gulp官網指出:
默認的,task 將以最大的并發數執行,也就是說,gulp 會一次性運行所有的 task 并且不做任何等待。
如果你想要創建一個序列化的 task 隊列(例如任務two依賴任務one),并以特定的順序執行,你需要做兩件事:
- 在 "one" 中,你加入一個提示,來告知什么時候它會完成:可以再完成時候返回一個 callback,或者返回一個 promise 或 stream,這樣系統會去等待它完成。
- 在 "two" 中,你需要添加一個提示來告訴系統它需要依賴第一個 task 完
代碼如下:
var gulp = require('gulp');
// 返回一個 callback,因此系統可以知道它什么時候完成
gulp.task('one', function(cb) {
// 做一些事 -- 異步的或者其他的
cb(err); // 如果 err 不是 null 或 undefined,則會停止執行,且注意,這樣代表執行失敗了
});
// 定義一個所依賴的 task 必須在這個 task 執行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后
});
gulp.task('default', ['one', 'two']);
但是親測了下,代碼如下:
var gulp = require('gulp');
var zip = require('gulp-zip');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
gulp.task("copy-common1",['clean'], function (cb) {
var err;
gulp.src(['client/**/**','!client/dev.html','!client/index.hbs','build/**/**'])
.pipe(rename(function (path) {
path.dirname += '';
}))
.pipe(gulp.dest("./dist/pages"))
cb(err)
})
//清空dist目錄
gulp.task("clean",function(cb){
var err;
console.log('清空 dist 目錄下的資源')
gulp.src('dist/*', {
read: false
})
.pipe(clean({
force: true
}));
cb(err)
})
//生成生產war包
gulp.task("package",['copy-common1'], function () {
gulp.src(['dist/**']).pipe(zip('dist.war')).pipe(gulp.dest('./'));
console.info('package ok!');
});
執行package任務,發現會拋出錯誤如下:
[13:59:55] Starting 'clean'...
清空 dist 目錄下的資源
[13:59:55] Finished 'clean' after 7.94 ms
[13:59:55] Starting 'copy-common1'...
[13:59:55] Finished 'copy-common1' after 3.43 ms
[13:59:55] Starting 'package'...
package ok!
[13:59:55] Finished 'package' after 2.08 ms
events.js:182
throw er; // Unhandled 'error' event
^
Error: ENOENT: no such file or directory, lstat '/dist/pages/common/libs/css/bootstrap.min.css'
但是如果是返回stream就會正常同步運行任務序列,修改如下:
var gulp = require('gulp');
var zip = require('gulp-zip');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
gulp.task("copy-common1", ['clean'], function (cb) {
return gulp.src(['client/**/**', '!client/dev.html', '!client/index.hbs', 'build/**/**'])
.pipe(rename(function (path) {
path.dirname += '';
}))
.pipe(gulp.dest("./dist/pages"))
})
//清空dist目錄
gulp.task("clean", function (cb) {
console.log('清空 dist 目錄下的資源')
return gulp.src('dist/*', {
read: false
})
.pipe(clean({
force: true
}));
})
//生成生產war包
gulp.task("package", ['copy-common1'], function () {
gulp.src(['dist/**']).pipe(zip('dist.war')).pipe(gulp.dest('./'));
console.info('package ok!');
});
執行package任務,工作流正常:
[14:07:07] Starting 'clean'...
清空 dist 目錄下的資源
[14:07:07] Finished 'clean' after 48 ms
[14:07:07] Starting 'copy-common1'...
[14:07:07] Finished 'copy-common1' after 127 ms
[14:07:07] Starting 'package'...
package ok!
[14:07:07] Finished 'package' after 1.85 ms
雖然問題已經解決了,但是會發現,我們這里的package任務依賴了copy-common1這個任務,copy-common1依賴了clean這個任務。這樣做其實相當于package這個任務隱式地包含了copy-common1與clean這兩個任務,這樣會帶來一些麻煩,比如我們已經通過gulp開啟了前端服務,dist目錄下已經是需要打包的資源了,只需要執行package打包下,上傳服務器。但是這里執行package任務時又執行了前兩個任務,先清掉再拷貝,重復了工作。還有很多其他的麻煩,不一一說明。
如何優雅地將gulp任務序列同步執行
針對這個問題,國內外都出現了插件進行處理,例如國內的 gulp-sequence
以及國外的 run-sequence
例如上面這個例子我們可以利用run-sequence
來優雅地解決順序執行同步任務,如下:
var gulp = require('gulp');
var zip = require('gulp-zip');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
var sequence = require('run-sequence');
gulp.task("copy-common1", function () {
return gulp.src(['client/**/**', '!client/dev.html', '!client/index.hbs', 'build/**/**'])
.pipe(rename(function (path) {
path.dirname += '';
}))
.pipe(gulp.dest("./dist/pages"))
})
//清空dist目錄
gulp.task("clean", function () {
console.log('清空 dist 目錄下的資源')
return gulp.src('dist/*', {
read: false
})
.pipe(clean({
force: true
}));
})
//生成生產war包
gulp.task("package", function () {
gulp.src(['dist/**']).pipe(zip('dist.war')).pipe(gulp.dest('./'));
console.info('package ok!');
});
gulp.task('runsequence', function (callback) {
sequence('clean', 'copy-common1', 'package', callback)
})
執行runsequence
任務,工作流正常:
[14:47:29] Starting 'runsequence'...
[14:47:29] Starting 'clean'...
清空 dist 目錄下的資源
[14:47:29] Finished 'clean' after 46 ms
[14:47:29] Starting 'copy-common1'...
[14:47:29] Finished 'copy-common1' after 131 ms
[14:47:29] Starting 'package'...
package ok!
[14:47:29] Finished 'package' after 7.93 ms
[14:47:29] Finished 'runsequence' after 191 ms
這里需要注意的是,這里也是需要返回一個stream。