用 Node.js 的 Stream 概念,gulp 实现了一套自己的 pipe,再包裹上 Promise 机制,让每个功能在一个接一个的管道中流过,宏观上实现我们需要它完成的 Task。Task 很好用,于是用了很多,结果 gulpfile 变得很长,每次修改就要费劲找好长时间,径管理也比较麻烦,很不好维护。是时候彻底重构一下了。
用 es6 替代 coffee
脚本原来是用 coffee 维护的,既能减少代码长度,又能避免未返回 gulp 对象所引起的断流问题。不过毕竟 coffee 已经不维护了,也没有自带的模块开发功能,所以这次决定改版成 es6 方式。
先引入 babel-core
和 babel-preset-es2015
这两个包
配置 package.json
...
"babel": {
"presets": [
"es2015"
]
}
...
es6 用的脚本名称不再是 gulpfile.js
,需要改成 gulpfile.babel.js
es6 环境搭建完成。
目录结构优化
fescripts
文件夹作为脚本主目录,只放一些配置文件及任务文件夹。
tasks
文件夹存放需要根据环境调用的任务。像 default.task.js
平时开发时调用,build.task.js
在服务器发布环境中运行。
cleaner
、compile
、debug
等文件夹存放具体小功能(就是原来 gulpfile.js
里的 task)。
配置文件说明 gulp.config.js
'use strict';
import Q from 'q';
module.exports = function ({ isDev = false, stage = "test" } = {}) {
let config = {
isDev: isDev,
system_list: (() => {
if (isDev) {
return [
"alphago/livecast/livecast-web/livecast-crm-webapp/src/main/webapp",
"alphago/livecast/livecast-web/livecast-student-webapp/src/main/webapp",
"alphago/livecast/livecast-web/livecast-teacher-webapp/src/main/webapp"
];
} else {
return [
`./build-repo/build-${stage}/livecast-crm-webapp/webroot`,
`./build-repo/build-${stage}/livecast-student-webapp/webroot`,
`./build-repo/build-${stage}/livecast-teacher-webapp/webroot`
];
}
})(),
great_promise: function(gulpJob, prams = true) {
let deferred = Q.defer();
let promiseArray = [];
this.system_list.forEach((base) => {
let def = Q.defer();
gulpJob(base, def);
promiseArray.push(def.promise);
});
Q.all(promiseArray).then(function () {
deferred.resolve(prams);
});
return deferred.promise;
},
colorful: function (word, $) {
let message = '';
Array.from(word, function (x, i) {
message += $.chalk[['red', 'yellow', 'green', 'blue', 'magenta', 'cyan', 'gray'][i%7]](x);
});
console.info(`[${ $.chalk.gray($.dateformat(new Date(), 'H:MM:ss')) }] >>> ${ message } <<<`);
},
jobStart: function ($, time, taskName, base = null) {
console.info(`[${ $.chalk.gray($.dateformat(time, 'H:MM:ss')) }] ${ $.chalk.red('Starting') } '${ $.chalk.green(taskName) }' ... ${ base != null ? ' ==> ' + $.chalk.yellow(base) : '' }`);
},
jobEnd: function ($, time, taskName, defTime, base = null) {
console.info(`[${ $.chalk.gray($.dateformat(time, 'H:MM:ss')) }] ${ $.chalk.blue('Finished') } '${ $.chalk.green(taskName) }' in ${ $.chalk.magenta(defTime + ' ms') } ${ base != null ? ' ==> ' + $.chalk.yellow(base) : '' }`);
}
};
return config;
};
参数说明
isDev 开发与发布环境辨别的参数,默认值给了false,也就是开发环境。
stage 发布环境状态的不同环境区分,例如: test、staging、release等
内部函数说明
system_list: 是立即执行函数,根据环境返回不同目录,最终是个数组。
great_promise: 解决同一任务便利不同系统目录,并并发直行用的。
jobStart & jobEnd:除了服务器脚本,其他所有的脚本都直接用 promise 实现,所以任务信息需要自己打出。
入口脚本配置 gulpfile.babel.js
'use strict';
import gulp from 'gulp';
import $ from 'gulp-load-plugins';
import config from './fescripts/gulp.config.js';
import Clean from './fescripts/tasks/clean.task.js';
import Build from './fescripts/tasks/build.task.js';
import Default from './fescripts/tasks/default.task.js';
gulp.task("clean", function () {
return Clean(gulp, config(), $({
pattern: ['gulp-*', 'gulp.*', 'chalk', 'dateformat', 'q'],
lazy: true
}), gulp.env.taskName || 'all');
});
gulp.task("build", function () {
return Build(gulp, config({
stage: process.argv.length >= 5 && process.argv[4] || 'test'
}), $({
pattern: ['gulp-*', 'gulp.*', 'chalk', 'dateformat', 'q'],
lazy: true
}));
});
gulp.task("watch", ['default'], function () {
return gulp.watch(['alphago/livecast/livecast-web/**/*.scss'], ['default']);
});
gulp.task("default", function () {
return Default(gulp, config({
isDev: true
}), $({
pattern: ['gulp-*', 'gulp.*', 'chalk', 'dateformat', 'q'],
lazy: true
}));
});
这里的 $ 不是 jQuery 的缩写,是 gulp 的一个插件 gulp-load-plugins
。
这插件会默认把 package.json
下 gulp-
& gulp.*
开头的插件全部加载到环境中。
如果需要,可以根据自己的需要修改 pattern
配置,加载更多其他插件。
编译脚本 build.task.js
'use strict';
import clean from './clean.task.js';
import css from './compile/css.compile.js';
import tools from './compile/tools.compile.js'
import js from './compile/js.compile.js';
import html from './compile/html.compile.js';
import formal from './debug/formal.mode.js';
module.exports = function (gulp, config, $, param = true) {
return $.q.fcall(() => true).then(function () {
return clean(gulp, config, $);
}).then(function () {
return css(gulp, config, $);
}).then(function () {
return tools(gulp, config, $);
}).then(function () {
return js(gulp, config, $);
}).then(function () {
return html(gulp, config, $);
}).then(function () {
return config.isDev ? formal(gulp, config, $) : true;
}).then(function () {
return param;
});
};
这里用到了 Node.js 的 q 模块
小任务脚本 css.compile.js
'use strict';
module.exports = function (gulp, config, $, param = true) {
return $.q.fcall(() => true).then(function () {
return config.great_promise(function (base, def) {
let startTime = new Date();
config.jobStart($, startTime, 'Make css version', base);
gulp.src([
`${base}/public/css/*.css`,
`!${base}/public/css/*-*.css`
])
.pipe($.plumber())
.pipe($.sourcemaps.write('maps', {
includeContent: false
}))
.pipe($.rev())
.pipe(gulp.dest(`${base}/public/css`))
.pipe($.rev.manifest())
.pipe(gulp.dest(`${base}/public/css`))
.on('end', function () {
let endTime = new Date();
config.jobEnd($, endTime, 'Make css version', endTime.getTime() - startTime.getTime(), base);
def.resolve();
});
});
}).then(function () {
return config.great_promise(function (base, def) {
let startTime = new Date();
config.jobStart($, startTime, 'Change links', base);
gulp.src([
`${base}/public/css/rev-manifest.json`,
`${base}/WEB-INF/livecast/layout/*links.ftl`
])
.pipe($.plumber())
.pipe($.if(/\.ftl$/, $.replace(/main(\S*).css/g, 'main.css')))
.pipe($.revCollector())
.pipe(gulp.dest(`${base}/WEB-INF/livecast/layout/`))
.on('end', function () {
let endTime = new Date();
config.jobEnd($, endTime, 'Change links', endTime.getTime() - startTime.getTime(), base);
def.resolve();
});
});
}).then(function () {
return param;
});
};
运行后效果
可以看到 Recover links
任务开始的顺序是 crm -> student -> teacher,最后完成顺序是 crm -> teacher -> student。
每个具体任务间是串行,三个项目目录间同一个任务是并行的。