gulp
gulp安装
1. 全局安装 gulp:
$ npm install --global gulp
2. 作为项目的开发依赖(devDependencies)安装:
$ npm install --save-dev gulp
3. 在项目根目录下创建一个名为 gulpfile.js 的文件:
var gulp = require('gulp');
gulp.task('default', function() {
// 将你的默认的任务代码放在这
});
4. 运行 gulp:
$ gulp
默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。
想要单独执行特定的任务(task),请输入 gulp <task> <othertask>。
下一步做什么呢?
你已经安装了所有必要的东西,并且拥有了一个空的 gulpfile。
我们可以通过一些例子来看gulp的使用
1.整合 streams 来处理错误
默认情况下,在 stream 中发生一个错误的话,它会被直接抛出,除非已经有一个时间监听器监听着 error 时间。 这在处理一个比较长的管道操作的时候会显得比较棘手。
通过使用 stream-combiner2,你可以将一系列的 stream 合并成一个,这意味着,你只需要在你的代码中一个地方添加监听器监听 error 时间就可以了。
这里是一个在 gulpfile 中使用它的例子
var combiner = require('stream-combiner2');
var uglify = require('gulp-uglify');
var gulp = require('gulp');
gulp.task('test', function() {
var combined = combiner.obj([
gulp.src('bootstrap/js/*.js'),
uglify(),
gulp.dest('public/bootstrap')
]);
// 任何在上面的 stream 中发生的错误,都不会抛出,
// 而是会被监听器捕获
combined.on('error', console.error.bind(console));
return combined;
});
删除文件和文件夹
你也许会想要在编译文件之前删除一些文件。由于删除文件和文件内容并没有太大关系,所以,我们没必要去用一个 gulp 插件。最好的一个选择就是使用一个原生的 node 模块。
因为 del 模块支持多个文件以及 globbing,因此,在这个例子中,我们将使用它来删除文件:
$ npm install --save-dev gulp del
假想有如下的文件结构:
. ├── dist │ ├── report.csv │ ├── desktop │ └── mobile │ ├── app.js │ ├── deploy.json │ └── index.html └── src
在 gulpfile 中,我们希望在运行我们的编译任务之前,将 mobile 文件的内容先清理掉:
var gulp = require('gulp');
var del = require('del');
gulp.task('clean:mobile', function (cb) {
del([
'dist/report.csv',
// 这里我们使用一个通配模式来匹配 `mobile` 文件夹中的所有东西
'dist/mobile/**/*',
// 我们不希望删掉这个文件,所以我们取反这个匹配模式
'!dist/mobile/deploy.json'
], cb);
});
gulp.task('default', ['clean:mobile']);
在管道中删除文件
你可能需要在管道中将一些处理过的文件删除掉。
我们使用 vinyl-paths 模块来简单地获取 stream 中每个文件的路径,然后传给 del 方法。
$ npm install --save-dev gulp del vinyl-paths
假想有如下的文件结构:
. ├── tmp │ ├── rainbow.js │ └── unicorn.js └── dist
var gulp = require('gulp');
var stripDebug = require('gulp-strip-debug'); // 仅用于本例做演示
var del = require('del');
var vinylPaths = require('vinyl-paths');
gulp.task('clean:tmp', function () {
return gulp.src('tmp/*')
.pipe(stripDebug())
.pipe(gulp.dest('dist'))
.pipe(vinylPaths(del));
});
gulp.task('default', ['clean:tmp']);
只有在已经使用了其他的插件之后才需要这样做,否则,请直接使用 gulp.src 来代替。
使用 watchify 加速 browserify 编译
当一个 browserify 项目开始变大的时候,编译打包的时间也会慢慢变得长起来。虽然开始的时候可能只需花 1 秒,然后当你的项目需要建立在一些流行的大型项目的基础上时,它很有可能就变成 30 秒了。
这就是为什么 substack 写了 watchify 的原因,一个持续监视文件的改动,并且 只重新打包必要的文件 的 browserify 打包工具。用这种方法,第一次打包的时候可能会还是会花 30 秒,但是后续的编译打包工作将一直保持在 100 毫秒以下 —— 这是一个极大的提升。
watchify 并没有一个相应的 gulp 插件,并且也不需要有:你可以使用 vinyl-source-stream 来把你的用于打包的 stream 连接到 gulp 管道中。
'use strict';
var watchify = require('watchify');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash.assign');
// 在这里添加自定义 browserify 选项
var customOpts = {
entries: ['./src/index.js'],
debug: true
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts));
// 在这里加入变换操作
// 比如: b.transform(coffeeify);
gulp.task('js', bundle); // 这样你就可以运行 `gulp js` 来编译文件了
b.on('update', bundle); // 当任何依赖发生改变的时候,运行打包工具
b.on('log', gutil.log); // 输出编译日志到终端
function bundle() {
return b.bundle()
// 如果有错误发生,记录这些错误
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
// 可选项,如果你不需要缓存文件内容,就删除
.pipe(buffer())
// 可选项,如果你不需要 sourcemaps,就删除
.pipe(sourcemaps.init({loadMaps: true})) // 从 browserify 文件载入 map
// 在这里将变换操作加入管道
.pipe(sourcemaps.write('./')) // 写入 .map 文件
.pipe(gulp.dest('./dist'));
}
增量编译打包,包括处理整所涉及的所有文件
在做增量编译打包的时候,有一个比较麻烦的事情,那就是你常常希望操作的是 所有 处理过的文件,而不仅仅是单个的文件。举个例子,你想要只对更改的文件做代码 lint 操作,以及一些模块封装的操作,然后将他们与其他已经 lint 过的,以及已经进行过模块封装的文件合并到一起。如果不用到临时文件的话,这将会非常困难。
使用 gulp-cached 以及 gulp-remember 来解决这个问题。
var gulp = require('gulp');
var header = require('gulp-header');
var footer = require('gulp-footer');
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var cached = require('gulp-cached');
var remember = require('gulp-remember');
var scriptsGlob = 'src/**/*.js';
gulp.task('scripts', function() {
return gulp.src(scriptsGlob)
.pipe(cached('scripts')) // 只传递更改过的文件
.pipe(jshint()) // 对这些更改过的文件做一些特殊的处理...
.pipe(header('(function () {')) // 比如 jshinting ^^^
.pipe(footer('})();')) // 增加一些类似模块封装的东西
.pipe(remember('scripts')) // 把所有的文件放回 stream
.pipe(concat('app.js')) // 做一些需要所有文件的操作
.pipe(gulp.dest('public/'));
});
gulp.task('watch', function () {
var watcher = gulp.watch(scriptsGlob, ['scripts']); // 监视与 scripts 任务中同样的文件
watcher.on('change', function (event) {
if (event.type === 'deleted') { // 如果一个文件被删除了,则将其忘记
delete cached.caches.scripts[event.path]; // gulp-cached 的删除 api
remember.forget('scripts', event.path); // gulp-remember 的删除 api
}
});
});
将 buffer 变为 stream (内存中的内容)
有时候,你会需要这样一个 stream,它们的内容保存在一个变量中,而不是在一个实际的文件中。换言之,怎么不使用 gulp.src() 而创建一个 'gulp' stream。
我们来举一个例子,我们拥有一个包含 js 库文件的目录,以及一个包含一些模块的不同版本文件的目录。编译的目标是为每个版本创建一个 js 文件,其中包含所有库文件以及相应版本的模块文件拼接后的结果。
逻辑上我们将把这个拆分为如下步骤:
- 载入库文件
- 拼接库文件的内容
- 载入不同版本的文件
- 对于每个版本的文件,将其和库文件的内容拼接
- 对于每个版本的文件,将结果输出到一个文件
想象如下的文件结构:
├── libs
│ ├── lib1.js
│ └── lib2.js
└── versions
├── version.1.js
└── version.2.js
你应该要得到这样的结果:
└── output
├── version.1.complete.js # lib1.js + lib2.js + version.1.js
└── version.2.complete.js # lib1.js + lib2.js + version.2.js
一个简单的模块化处理方式将会像下面这样:
var gulp = require('gulp');
var runSequence = require('run-sequence');
var source = require('vinyl-source-stream');
var vinylBuffer = require('vinyl-buffer');
var tap = require('gulp-tap');
var concat = require('gulp-concat');
var size = require('gulp-size');
var path = require('path');
var es = require('event-stream');
var memory = {}; // 我们会将 assets 保存到内存中
// 载入内存中文件内容的任务
gulp.task('load-lib-files', function() {
// 从磁盘中读取库文件
return gulp.src('src/libs/*.js')
// 将所有库文件拼接到一起
.pipe(concat('libs.concat.js'))
// 接入 stream 来获取每个文件的数据
.pipe(tap(function(file) {
// 保存文件的内容到内存
memory[path.basename(file.path)] = file.contents.toString();
}));
});
gulp.task('load-versions', function() {
memory.versions = {};
// 从磁盘中读取文件
return gulp.src('src/versions/version.*.js')
// 接入 stream 来获取每个文件的数据
.pipe( tap(function(file) {
// 在 assets 中保存文件的内容
memory.versions[path.basename(file.path)] = file.contents.toString();
}));
});
gulp.task('write-versions', function() {
// 我们将不容版本的文件的名字保存到一个数组中
var availableVersions = Object.keys(memory.versions);
// 我们创建一个数组来保存所有的 stream 的 promise
var streams = [];
availableVersions.forEach(function(v) {
// 以一个假文件名创建一个新的 stream
var stream = source('final.' + v);
// 从拼接后的文件中读取数据
var fileContents = memory['libs.concat.js'] +
// 增加版本文件的数据
'\n' + memory.versions[v];
streams.push(stream);
// 将文件的内容写入 stream
stream.write(fileContents);
process.nextTick(function() {
// 在下一次处理循环中结束 stream
stream.end();
});
stream
// 转换原始数据到 stream 中去,到一个 vinyl 对象/文件
.pipe(vinylBuffer())
//.pipe(tap(function(file) { /* 这里可以做一些对文件内容的处理操作 */ }))
.pipe(gulp.dest('output'));
});
return es.merge.apply(this, streams);
});
//============================================ 我们的主任务
gulp.task('default', function(taskDone) {
runSequence(
['load-lib-files', 'load-versions'], // 并行载入文件
'write-versions', // 一旦所有资源进入内存便可以做写入操作了
taskDone // 完成
);
});
//============================================ 我们的监控任务
// 只在运行完 'default' 任务后运行,
// 这样所有的资源都已经在内存中了
gulp.task('watch', ['default'], function() {
gulp.watch('./src/libs/*.js', function() {
runSequence(
'load-lib-files', // 我们只需要载入更改过的文件
'write-versions'
);
});
gulp.watch('./src/versions/*.js', function() {
runSequence(
'load-versions', // 我们只需要载入更改过的文件
'write-versions'
);
});
});
在 gulp 中运行 Mocha 测试
运行所有的测试用例
// npm install gulp gulp-mocha
var gulp = require('gulp');
var mocha = require('gulp-mocha');
gulp.task('default', function() {
return gulp.src(['test/test-*.js'], { read: false })
.pipe(mocha({
reporter: 'spec',
globals: {
should: require('should')
}
}));
});
在文件改动时候运行 mocha 测试用例
// npm install gulp gulp-mocha gulp-util
var gulp = require('gulp');
var mocha = require('gulp-mocha');
var gutil = require('gulp-util');
gulp.task('mocha', function() {
return gulp.src(['test/*.js'], { read: false })
.pipe(mocha({ reporter: 'list' }))
.on('error', gutil.log);
});
gulp.task('watch-mocha', function() {
gulp.watch(['lib/**', 'test/**'], ['mocha']);
});

浙公网安备 33010602011771号