Gulp的使用

什么是 Gulp?

Gulp 的官网title上对这个工具有一个比较准确的定义,叫做:基于流的自动化构建工具。如果你查看它的网页源代码,还会看到在标签里有一段更详细的描述:

1
Gulp.js 是一个自动化构建工具,开发者可以使用它在项目开发过程中自动执行常见任务。Gulp.js 是基于 Node.js 构建的,利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。Gulp.js 源文件和你用来定义任务的 Gulp 文件都是通过 JavaScript(或者 CoffeeScript )源码来实现的。

百度百科维基百科上都搜不到相关介绍,

百度百科是这样:

百度百科gulp

维基百科是这样:

维基百科gulp

这都什么鬼。 还是老老实实的看官网吧 点这里进中文官网

关于gulp的特点:

易于使用

通过代码优于配置的策略,Gulp 让简单的任务简单,复杂的任务可管理。

构建快速

利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。

插件高质

Gulp 严格的插件指南确保插件如你期望的那样简洁高质得工作。

易于学习

通过最少的 API,掌握 Gulp 毫不费力,构建工作尽在掌握:如同一系列流管道。

由于 Gulp 是基于流的,所以 Gulp 对于文件内容的操作就像是水槽对于水流,水流流经水槽,水槽将水流塑造成不同的形状。既然是基于流的,在进一步理解 Gulp 前,我们最好先来理解什么是流。

在计算机系统中文件的大小是变化很大的,有的文件内容非常多非常大,而 Node.js 里文件操作是异步的,如果用一个形象的比喻来形容将一个文件的内容读取出来并写入另一个文件中,可以将它想象成文件内容像是水流,从一个文件“流”入另一个文件。

流

在node里面,读写文件可以用“流”来描述:

  • “use strict”;

  • let fs = require(“fs”);

  • fs.createReadStream(“./in.txt”)

  • .pipe(fs.createWriteStream(“./out.txt”));

上面的代码除了将 in.txt 文件中的内容输出到 out.txt 之外,不做其他任何事情,相当于复制了一份数据,从语法形式上可以看到,“数据流”从 fs.createReadStream 创建然后经过 pipe 流出,最后到 fs.createWriteStream。

Gulp的使用

Gulp的官方文档写的很简单,打开文文档页面除了让你看中文文档、入门指南、API、插件开发、常见问题、使用技巧之外就没什么了,对于新手来说完全够用,但实际上真要用 Gulp 的高级功能,这些文档简直就和教人如何画马一样:

怎样画马

既然 Gulp 是基于流的,我们就要理解 Gulp 如何控制和操作流。

1
2
3
4
5
6
7
8
9
10
11
12
var gulp = require("gulp");

gulp.task("sync1", function() {
console.log("我是一个同步任务");
});

gulp.task("async", function(done) {
setTimeout(function(){
console.log("我是一个异步任务");
done();
}, 2000);
});

我们可以看到 Gulp 是基于任务的,gulp.task 可以定义一个任务,这样的话,我们在命令行下就可以通过 gulp 任务名 的方式来执行命令了:

1
2
3
4
5
在终端运行:gulp sync1
[18:27:12] Using gulpfile ~/Workspace/yuntu/myblog-master/item/photohome/gulpfile.js
[18:27:12] Starting "sync1"...
我是一个同步任务
[18:27:12] Finished "sync1" after 122 μs
1
2
3
4
在终端运行:gulp async
[18:27:48] Using gulpfile ~/Workspace/yuntu/myblog-master/item/photohome/gulpfile.js
[18:27:48] Starting "async"... 我是一个异步任务
[18:27:50] Finished "async" after 2 s

Gulp 的任务可以是同步和异步,在异步任务中确定任务完成,可以通过调用函数参数 done 来实现。

Gulp 也允许我们将任务组合起来执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var gulp = require("gulp");
var through = require("through2");

gulp.task("sync1", function() {
console.log("我是一个同步任务");
});

gulp.task("sync2", function() {
console.log("我是另一个同步任务");
});

gulp.task("sync3", function() {
console.log("我是又一个同步任务");
});

gulp.task("async", function(done) {
console.log("老大喊我去搬砖");
setTimeout(function(){
console.log("我是一个异步任务");
done();
}, 2000);
});

gulp.task("syncs", ["async", "sync1", "sync2", "sync3"],
function(){
console.log("砖搬完了!");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
终端运行: gulp syncs
[18:30:30] Using gulpfile ~/Workspace/yuntu/myblog-master/item/photohome/gulpfile.js
[18:30:30] Starting "async"...
老大喊我去搬砖
[18:30:30] Starting "sync1"...
我是一个同步任务
[18:30:30] Finished "sync1" after 142 μs
[18:30:30] Starting "sync2"...
我是另一个同步任务
[18:30:30] Finished "sync2" after 55 μs
[18:30:30] Starting "sync3"...
我是又一个同步任务
[18:30:30] Finished "sync3" after 43 μs
我是一个异步任务
[18:30:32] Finished "async" after 2 s
[18:30:32] Starting "syncs"...
砖搬完了!
[18:30:32] Finished "syncs" after 38 μs

我们看到说 gulp.task 可以有依赖,只要第二个参数传一个数组,中间加上依赖的任务就行了,而数组里面的这些任务是并行处理的,不会一个执行完才执行另一个(同步任务的输出比异步任务的结束早)。

以上是 Gulp 基本的任务模型。对于每个 task,Gulp 通常用来操作文件输入和输出流,因此 Gulp 封装了批量操作文件流的 api:

1
2
3
4
gulp.task("src-dist", function(){
gulp.src("./*.html")
.pipe(gulp.dest("./dist"));
});

上面的命令表示将当前目录下所有的 .html 文件匹配出来,依次输出到目标文件夹 ./dist 中去。

我们还可以用更高级的通配符:

1
2
3
4
gulp.task("src-dist", function(){
gulp.src("./**/*.html")
.pipe(gulp.dest("./dist"));
});

这样处理的 html 文件不仅仅匹配当前目录下的,还包括所有子目录里。关于输入这块,具体的用法还有很多,遵循的规范是glob模式,可以参考node-glob

处理文件的内容
与上面说的 FileSystem 文件流类似,如果我们不做什么别的事情,那么我们就只是将文件从源 src,拷贝到了目的地 dest,其他的啥也没做,那么显然,我们可以做那么一些事情,在这里,我们尝试处理一下 index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
gulp.task("build-index", function(){
gulp.src("./index.html")
.pipe(through.obj(function(file, encode, cb) {
var contents = file.contents.toString(encode);

var HTMLMinifier = require("html-minifier").minify;

var minified = HTMLMinifier(contents, {
minifyCSS: true,
minifyJS: true,
collapseWhitespace: true,
removeAttributeQuotes: true
});

//console.log(minified);
file.contents = new Buffer(minified, encode);
cb(null, file, encode);
}))
.pipe(gulp.dest("./dist"));
});

gulp.src 的输入流和 fileReadStream 会有一点点不一样,它的第一个参数不是一个 Buffer,而是一个包含文件信息和文件内容的对象,第二个参数是文件的编码,因此我们可以通过

var contents = file.contents.toString(encode);
将文件内容转成字符串。之后,我们使用 html-minifier 对文件的HTML内容和内联的样式、脚本进行压缩,这样就简单完成了首页 index.html 的优化!

优化

Gulp可以帮助我们合并多个JS文件、快速压缩JS\CSS等文件;

合并JS

首先我们需要安装Gulp的插件:gulp-concat

https://www.npmjs.com/package/gulp-concat/

安装方法:npm install gulp-jsmin –save-dev

使用方法:

1
2
3
4
5
6
7
8
9
10
var gulp = require('gulp');
var jsmin = require('gulp-jsmin');
var rename = require('gulp-rename');

gulp.task('default', function () {
gulp.src('src/**/*.js')
.pipe(jsmin())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('dist'));
});<、pre>

压缩JS:首先我们需要安装Gulp的插件:gulp-jsmin

https://www.npmjs.com/package/gulp-jsmin/

安装方法:npm install gulp-jsmin –save-dev

使用方法:

1
2
3
4
5
6
7
8
9
10
var gulp = require('gulp');
var jsmin = require('gulp-jsmin');
var rename = require('gulp-rename');

gulp.task('default', function () {
gulp.src('src/**/*.js')
.pipe(jsmin())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('dist'));
});

压缩CSS:

首先我们需要安装Gulp的插件:gulp-cssmin

https://www.npmjs.com/package/gulp-cssmin

安装方法:npm install gulp-cssmin –save-dev

使用方法:

1
2
3
4
5
6
7
8
9
10
var gulp = require('gulp');
var cssmin = require('gulp-cssmin');
var rename = require('gulp-rename');

gulp.task('default', function () {
gulp.src('src/**/*.css')
.pipe(cssmin())
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('dist'));
});

总结

Gulp 是一个非常简单的构建脚本,它可以压缩合并我们项目的 js 和 css 并处理小图片,我们还可以给它进一步增加其他功能,例如给压缩的文件添加版本号,或者根据内容计算签名以实现更新后不被缓存,我们还可以用 CDN 服务的 sdk 将资源发布到 CDN 并替换原始链接,同时,我们可以不用每次发布所有的文件,我们可以在开发的时候用 gulp.watch 来监控文件的修改,以实现增量的编译发布。

总之,我们可以用 gulp 来做许多有用的事情,来完善我们的构建脚本,而这一切都因为 gulp 基于流的构建以及 NPM 丰富的库变得非常简单。事实上 gulp 提供了不少有用的插件,这些插件直接返回 stream 对象,可以让构建过程变得更简单,具体的可以多研究官方的文档。