關于同步運行gulp任務那些事

背景

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。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容