如何批量处理相似的 gulp 任务以减少代码重复
How to batch similar gulp tasks to reduce code repetition
我写了一个 gulp 文件来监视几个目录的变化,然后创建到多个指定目标的串联。
这是我的项目结构的简化版本:
我有 2 个站点文件夹:
一个/和两个/
每个站点都有两个分支文件夹:
a/ 和 b/
在每个分支中,有三个文件夹:
内部/、外部/ 和 网络/
我的任务是从内部和外部文件夹中抓取相似的零件文件,并将它们连接到相关的网络文件夹中。下面是所需输出的简单示例。
-- inner/
|-- color1
|-- color2
|-- fruit1
|-- fruit2
-- outer/
|-- color1
|-- color2
|-- fruit1
|-- fruit2
-- web/
|-- colors.txt
|-- fruits.txt
我创建了一个 config.json 文件来保存特定于站点的配置。目前仅使用它来自定义站点路径。这里是 config.json
{
"sites": {
"one": {
"a": "/path/to/one/a/",
"b": "/path/to/one/b/"
},
"two": {
"a": "/path/to/two/a/",
"b": "/path/to/two/b/"
}
}
}
最后是 gulpfile.js
// Include local Gulp
var gulp = require("gulp");
// Get data from config.json
var sites = require("./config.json").sites;
// Include Gulp specific plugins
var gConcat = require("gulp-concat");
var gHeader = require("gulp-header");
var gUtil = require("gulp-util");
var gNotify = require("gulp-notify");
// Setup directories
var outer = "outer/";
var inner = "inner/";
var web = "web/";
// Misc
var alertMessage = "# GENERATED FILE - DO NOT MODIFY\n\n";
// 8 total tasks for concatenation
// Concatenate to colors.txt - 4 tasks
// Color task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_color", function() {
return gulp.src([sites.one.a + outer + "color?", sites.one.a + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.a + web))
.pipe(gNotify());
});
// Color task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_color", function() {
return gulp.src([sites.one.b + outer + "color?", sites.one.b + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.b + web))
.pipe(gNotify());
});
// Color task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_color", function() {
return gulp.src([sites.two.a + outer + "color?", sites.two.a + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.a + web))
.pipe(gNotify());
});
// Color task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_color", function() {
return gulp.src([sites.two.b + outer + "color?", sites.two.b + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.b + web))
.pipe(gNotify());
});
// Concatenate to fruits.txt - 4 tasks
// Fruit task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_fruit", function() {
return gulp.src([sites.one.a + outer + "fruit?", sites.one.a + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.a + web))
.pipe(gNotify());
});
// Fruit task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_fruit", function() {
return gulp.src([sites.one.b + outer + "fruit?", sites.one.b + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.b + web))
.pipe(gNotify());
});
// Fruit task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_fruit", function() {
return gulp.src([sites.two.a + outer + "fruit?", sites.two.a + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.a + web))
.pipe(gNotify());
});
// Fruit task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_fruit", function() {
return gulp.src([sites.two.b + outer + "fruit?", sites.two.b + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.b + web))
.pipe(gNotify());
});
// Watch for all events in specified {directories}/{files}, then trigger appropriate task
// 8 total watch jobs
gulp.task("watch", function () {
// Color related watch jobs - Total 4
// Color watch 1: [ Site => one ] [ Branch => a ]
gulp.watch([sites.one.a + outer + "**/color?", sites.one.a + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_a_color");
});
// Color watch 2: [ Site => one ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_b_color");
});
// Color watch 3: [ Site => two ] [ Branch => a ]
gulp.watch([sites.two.a + outer + "**/color?", sites.two.a + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_a_color");
});
// Color watch 4: [ Site => two ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_b_color");
});
// Fruit related watch jobs - Total 4
// Fruit watch 1: [ Site => one ] [ Branch => a ]
gulp.watch([sites.one.a + outer + "**/fruit?", sites.one.a + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_a_fruit");
});
// Fruit watch 2: [ Site => one ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_b_fruit");
});
// Fruit watch 3: [ Site => two ] [ Branch => a ]
gulp.watch([sites.two.a + outer + "**/fruit?", sites.two.a + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_a_fruit");
});
// Fruit watch 4: [ Site => two ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_b_fruit");
});
});
// Run all tasks
gulp.task("background",
[
"one_a_color", "one_b_color", "two_a_color", "two_b_color",
"one_a_fruit", "one_b_fruit", "two_a_fruit", "two_b_fruit",
"watch"
]
);
上面的 gulp 文件可以正常工作。但是,如您所见,大部分代码都是重复的,只有 gulp.src 和 gulp.dest 以及任务名称发生变化的部分。
我的问题是。是否可以简化这个 gulp 文件,而不是为每个任务重复代码,也许可以将类似的任务批处理在一起。
不是那么容易的任务,但让我们看看我们是否可以优化它。 Gulp 和 Globs 大量处理数组,这就是为什么我们必须先将您的路径转换为数组:
var gulp = require('gulp');
var concat = require('gulp-concat');
var es = require('event-stream');
var sites = require('./config.json').sites;
var toArray = function(conf) {
var arr = [];
for(var key in conf) {
if(typeof conf[key] === 'object') {
arr = arr.concat(toArray(conf[key]));
} else {
arr.push(conf[key]);
}
}
return arr;
};
var sites = toArray(sites);
现在我们有了路径,我们为水果和颜色创建球体。
var globs = [];
sites.forEach(function(data) {
globs.push(data + '**/color*');
globs.push(data + '**/fruit*');
});
根据您当前的配置,您将获得一个包含 8 个条目的数组。接下来,让我们定义连接任务。这就是你把 "batched" 放在一起的意思,我们需要一个所谓的流数组(我写过 here)。它是现有数组到许多 gulp 流的简单映射,这些流在最后通过 event-stream
模块合并。随着 color/fruit 的进行,我们需要对 concat 名称和目标名称进行一些创意。
请注意,我使用 changed
插件来防止无用的构建。
gulp.task('concat', function() {
var tasks = globs.map(function(glob) {
var file = glob.indexOf('color') >= 0 ? 'col' : 'fru';
var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(gulp.dest(dest))
});
return es.merge.apply(null, tasks);
});
这个任务现在完成了我们需要的一切,而且是渐进式的。所以我们的watch过程还是比较简单的。
gulp.task('watch', ['concat'], function() {
gulp.watch(globs, ['concat']);
});
希望对您有所帮助!
更新
好的,我做了一些调整,应该可以避免重建整个项目。
首先,我将 concatStream 提取到一个函数中。这实际上是您已经对自己的样本所做的一件事:
var concatStream = function(glob) {
var file = glob.indexOf('color') >= 0 ? 'farbe' : 'frucht';
var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(header(alertMessage))
.pipe(notify())
.pipe(gulp.dest(dest))
};
根据 Glob(我们 select 目录中的颜色或水果的文件模式),我们定义了一个新的输出(文件,当 'color' 在我们的搜索字符串,'fru' 否则)和一个新的目的地(这只是没有 colors/fruits 搜索模式的旧文件夹)。
gulp.task('concat') 现在执行以下操作:
gulp.task('concat', function() {
var tasks = globs.map(concatStream);
return es.merge.apply(null, tasks);
});
我们的每个 glob(console.log 它们,如果你想知道里面有什么)被映射到 concatStream,然后新的流数组被合并并执行。
watch 任务现在是新的...我们做的与我们的 'concat' 任务有点相同:
gulp.task('watch', ['concat'], function() {
globs.map(function(glob) {
gulp.watch(glob, function() {
return concatStream(glob);
})
})
});
对于每个 glob,我们创建一个新的观察者,它只是再次调用 concatStream。
更新
小改动
在 glob 中,将通配符 (*) 更改为可选的单字符匹配 (?),将允许我们对输出文件使用相同的名称(例如颜色和水果)。
var globs = [];
sites.forEach(function(data) {
globs.push(data + '**/color?');
globs.push(data + '**/fruit?');
});
还有这个...
var concatStream = function(glob) {
var file = glob.indexOf('color') >= 0 ? 'color' : 'fruit';
var dest = glob.replace('**/color?','').replace('**/fruit?','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(header(alertMessage))
.pipe(notify())
.pipe(gulp.dest(dest))
};
现在我可以为我的输出文件保留 color 和 fruit 的名称,而不用担心 glob 匹配名称并添加它的现有内容回到文件
我写了一个 gulp 文件来监视几个目录的变化,然后创建到多个指定目标的串联。
这是我的项目结构的简化版本:
我有 2 个站点文件夹: 一个/和两个/
每个站点都有两个分支文件夹: a/ 和 b/
在每个分支中,有三个文件夹: 内部/、外部/ 和 网络/
我的任务是从内部和外部文件夹中抓取相似的零件文件,并将它们连接到相关的网络文件夹中。下面是所需输出的简单示例。
-- inner/
|-- color1
|-- color2
|-- fruit1
|-- fruit2
-- outer/
|-- color1
|-- color2
|-- fruit1
|-- fruit2
-- web/
|-- colors.txt
|-- fruits.txt
我创建了一个 config.json 文件来保存特定于站点的配置。目前仅使用它来自定义站点路径。这里是 config.json
{
"sites": {
"one": {
"a": "/path/to/one/a/",
"b": "/path/to/one/b/"
},
"two": {
"a": "/path/to/two/a/",
"b": "/path/to/two/b/"
}
}
}
最后是 gulpfile.js
// Include local Gulp
var gulp = require("gulp");
// Get data from config.json
var sites = require("./config.json").sites;
// Include Gulp specific plugins
var gConcat = require("gulp-concat");
var gHeader = require("gulp-header");
var gUtil = require("gulp-util");
var gNotify = require("gulp-notify");
// Setup directories
var outer = "outer/";
var inner = "inner/";
var web = "web/";
// Misc
var alertMessage = "# GENERATED FILE - DO NOT MODIFY\n\n";
// 8 total tasks for concatenation
// Concatenate to colors.txt - 4 tasks
// Color task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_color", function() {
return gulp.src([sites.one.a + outer + "color?", sites.one.a + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.a + web))
.pipe(gNotify());
});
// Color task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_color", function() {
return gulp.src([sites.one.b + outer + "color?", sites.one.b + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.b + web))
.pipe(gNotify());
});
// Color task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_color", function() {
return gulp.src([sites.two.a + outer + "color?", sites.two.a + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.a + web))
.pipe(gNotify());
});
// Color task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_color", function() {
return gulp.src([sites.two.b + outer + "color?", sites.two.b + inner + "color?"])
.pipe(gConcat("colors.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.b + web))
.pipe(gNotify());
});
// Concatenate to fruits.txt - 4 tasks
// Fruit task 1: [ Site => one ] [ Branch => a ]
gulp.task("one_a_fruit", function() {
return gulp.src([sites.one.a + outer + "fruit?", sites.one.a + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.a + web))
.pipe(gNotify());
});
// Fruit task 2: [ Site => one ] [ Branch => b ]
gulp.task("one_b_fruit", function() {
return gulp.src([sites.one.b + outer + "fruit?", sites.one.b + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.one.b + web))
.pipe(gNotify());
});
// Fruit task 3: [ Site => two ] [ Branch => a ]
gulp.task("two_a_fruit", function() {
return gulp.src([sites.two.a + outer + "fruit?", sites.two.a + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.a + web))
.pipe(gNotify());
});
// Fruit task 4: [ Site => two ] [ Branch => b ]
gulp.task("two_b_fruit", function() {
return gulp.src([sites.two.b + outer + "fruit?", sites.two.b + inner + "fruit?"])
.pipe(gConcat("fruits.txt"))
.pipe(gHeader(alertMessage))
.pipe(gulp.dest(sites.two.b + web))
.pipe(gNotify());
});
// Watch for all events in specified {directories}/{files}, then trigger appropriate task
// 8 total watch jobs
gulp.task("watch", function () {
// Color related watch jobs - Total 4
// Color watch 1: [ Site => one ] [ Branch => a ]
gulp.watch([sites.one.a + outer + "**/color?", sites.one.a + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_a_color");
});
// Color watch 2: [ Site => one ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_b_color");
});
// Color watch 3: [ Site => two ] [ Branch => a ]
gulp.watch([sites.two.a + outer + "**/color?", sites.two.a + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_a_color");
});
// Color watch 4: [ Site => two ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/color?", sites.one.b + inner + "**/color?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_b_color");
});
// Fruit related watch jobs - Total 4
// Fruit watch 1: [ Site => one ] [ Branch => a ]
gulp.watch([sites.one.a + outer + "**/fruit?", sites.one.a + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_a_fruit");
});
// Fruit watch 2: [ Site => one ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("one_b_fruit");
});
// Fruit watch 3: [ Site => two ] [ Branch => a ]
gulp.watch([sites.two.a + outer + "**/fruit?", sites.two.a + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_a_fruit");
});
// Fruit watch 4: [ Site => two ] [ Branch => b ]
gulp.watch([sites.one.b + outer + "**/fruit?", sites.one.b + inner + "**/fruit?"], function(event) {
gUtil.log(event.path.split("/").pop(), "=>", event.type);
gulp.start("two_b_fruit");
});
});
// Run all tasks
gulp.task("background",
[
"one_a_color", "one_b_color", "two_a_color", "two_b_color",
"one_a_fruit", "one_b_fruit", "two_a_fruit", "two_b_fruit",
"watch"
]
);
上面的 gulp 文件可以正常工作。但是,如您所见,大部分代码都是重复的,只有 gulp.src 和 gulp.dest 以及任务名称发生变化的部分。
我的问题是。是否可以简化这个 gulp 文件,而不是为每个任务重复代码,也许可以将类似的任务批处理在一起。
不是那么容易的任务,但让我们看看我们是否可以优化它。 Gulp 和 Globs 大量处理数组,这就是为什么我们必须先将您的路径转换为数组:
var gulp = require('gulp');
var concat = require('gulp-concat');
var es = require('event-stream');
var sites = require('./config.json').sites;
var toArray = function(conf) {
var arr = [];
for(var key in conf) {
if(typeof conf[key] === 'object') {
arr = arr.concat(toArray(conf[key]));
} else {
arr.push(conf[key]);
}
}
return arr;
};
var sites = toArray(sites);
现在我们有了路径,我们为水果和颜色创建球体。
var globs = [];
sites.forEach(function(data) {
globs.push(data + '**/color*');
globs.push(data + '**/fruit*');
});
根据您当前的配置,您将获得一个包含 8 个条目的数组。接下来,让我们定义连接任务。这就是你把 "batched" 放在一起的意思,我们需要一个所谓的流数组(我写过 here)。它是现有数组到许多 gulp 流的简单映射,这些流在最后通过 event-stream
模块合并。随着 color/fruit 的进行,我们需要对 concat 名称和目标名称进行一些创意。
请注意,我使用 changed
插件来防止无用的构建。
gulp.task('concat', function() {
var tasks = globs.map(function(glob) {
var file = glob.indexOf('color') >= 0 ? 'col' : 'fru';
var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(gulp.dest(dest))
});
return es.merge.apply(null, tasks);
});
这个任务现在完成了我们需要的一切,而且是渐进式的。所以我们的watch过程还是比较简单的。
gulp.task('watch', ['concat'], function() {
gulp.watch(globs, ['concat']);
});
希望对您有所帮助!
更新
好的,我做了一些调整,应该可以避免重建整个项目。
首先,我将 concatStream 提取到一个函数中。这实际上是您已经对自己的样本所做的一件事:
var concatStream = function(glob) {
var file = glob.indexOf('color') >= 0 ? 'farbe' : 'frucht';
var dest = glob.replace('**/color*','').replace('**/fruit*','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(header(alertMessage))
.pipe(notify())
.pipe(gulp.dest(dest))
};
根据 Glob(我们 select 目录中的颜色或水果的文件模式),我们定义了一个新的输出(文件,当 'color' 在我们的搜索字符串,'fru' 否则)和一个新的目的地(这只是没有 colors/fruits 搜索模式的旧文件夹)。 gulp.task('concat') 现在执行以下操作:
gulp.task('concat', function() {
var tasks = globs.map(concatStream);
return es.merge.apply(null, tasks);
});
我们的每个 glob(console.log 它们,如果你想知道里面有什么)被映射到 concatStream,然后新的流数组被合并并执行。
watch 任务现在是新的...我们做的与我们的 'concat' 任务有点相同:
gulp.task('watch', ['concat'], function() {
globs.map(function(glob) {
gulp.watch(glob, function() {
return concatStream(glob);
})
})
});
对于每个 glob,我们创建一个新的观察者,它只是再次调用 concatStream。
更新
小改动
在 glob 中,将通配符 (*) 更改为可选的单字符匹配 (?),将允许我们对输出文件使用相同的名称(例如颜色和水果)。
var globs = [];
sites.forEach(function(data) {
globs.push(data + '**/color?');
globs.push(data + '**/fruit?');
});
还有这个...
var concatStream = function(glob) {
var file = glob.indexOf('color') >= 0 ? 'color' : 'fruit';
var dest = glob.replace('**/color?','').replace('**/fruit?','') + 'web';
return gulp.src(glob)
.pipe(concat(file + '.txt'))
.pipe(header(alertMessage))
.pipe(notify())
.pipe(gulp.dest(dest))
};
现在我可以为我的输出文件保留 color 和 fruit 的名称,而不用担心 glob 匹配名称并添加它的现有内容回到文件