是时候抛弃繁重的Grunt了。Gulp是一个直观的、配置的、基于流的任务发布系统,而且它更高效。

gulp

为什么我会感兴趣呢?好问题。Gulp通过配置写代码不仅使得它编写任务简单,而且更加方便阅读和维护。

Gulp运用node.js的流,这使得它构建任务很快,因为没有磁盘文件的读写操作,如果你想了解更多关于流的知识,你可以看看这个。Gulp允许你输入源文件,然后在一系列的管道插件中处理,最后输出,不像Grunt你需要为每个插件配置输入和输出。下面就让我们通过一个sass编译的例子来看看Gulp和Grunt的差异吧。

Grunt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
'dist/assets/css/main.css': 'src/styles/main.scss',
}
}
},

autoprefixer: {
dist: {
options: {
browsers: [
'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
]
},
src: 'dist/assets/css/main.css',
dest: 'dist/assets/css/main.css'
}
},

grunt.registerTask('styles', ['sass', 'autoprefixer']);

Grunt要求每个插件配置要相互独立、要分别为每个插件配置输入源和输出路径。如,我们在sass插件里面配置了一个输入文件,然后保存输出。接着我们需要配置Autoprefixer的输入为Sass的输出,然后再输出了一个文件。让我们来看看Gulp是怎么做的:

Gulp:

1
2
3
4
5
6
gulp.task('sass', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'compressed' }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/assets/css'))
});

在Gulp中我们只配置一次输入文件,然后依次通过Sass插件处理,再传给Autoprefixer插件处理,然后我们得到输出文件。整个过程没有读取和写入不必要的文件,效率大大提高。

因此,你感兴趣了么?让我们从安装Gulp,创建基本的任务配置文件gulpfile开始吧。

安装gulp

在我们开始配置任务之前,我们先要安装gulp:

1
npm install gulp -g

这样gulp就以全局的方式安装了,你可以在任何node命令行里面调用gulp CLI。然后我们需要在本地的某个项目里面使用gulp。使用cd命令进入到项目目录,运行下面的命令(先确保项目目录存在package.json文件):

1
npm install gulp --save-dev

这会把gulp安装到本地项目,并且把依赖的包写入到package.json文件的devDependencies里面

安装gulp插件

我们将会安装下列插件来开始我们的任务:

运行下面的命令安装这些插件:

1
npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev

这将会安装所有的依赖插件,并写入到package.json的devDependencies里面。所有的gulp插件列表可以在这里看到。

加载插件

我们需要创建一个gulpfile.js,然后使用这些插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
var gulp = require('gulp'),
sass = require('gulp-ruby-sass'),
autoprefixer = require('gulp-autoprefixer'),
minifycss = require('gulp-minify-css'),
jshint = require('gulp-jshint'),
uglify = require('gulp-uglify'),
imagemin = require('gulp-imagemin'),
rename = require('gulp-rename'),
concat = require('gulp-concat'),
notify = require('gulp-notify'),
cache = require('gulp-cache'),
livereload = require('gulp-livereload'),
del = require('del');

我们也可以像grunt那样自动加载插件:auto load

创建任务

编译sass、加前缀、压缩

1
2
3
4
5
6
7
8
9
10
gulp.task('styles', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'expanded' }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/assets/css'))
.pipe(rename({suffix: '.min'}))
.pipe(minifycss())
.pipe(gulp.dest('dist/assets/css'))
.pipe(notify({ message: '样式任务完成' }));
});

sass({ style: 'expanded' }:编译后保留原格式

1
gulp.task('styles', function() { ... )};

gulp.taskAPI是用来创建任务的。然后通过命令gulp styles运行这个任务。

1
return gulp.src('src/styles/main.scss')

gulp.srcAPI用来配置输入的源文件。也可以用模式匹配,如/**/*.scss匹配所有文件夹下面后缀为.scss的文件作为输入。通过返回流使得它是异步的,确保在提醒任务完成的时候任务是完成了的。

1
.pipe(sass({ style: 'expanded' }))

通过.pipe()把源文件流入一个插件的管道中。然后我们可以去插件的官网看看这个插件的详细用法。

1
.pipe(gulp.dest('dist/assets/css'));

gulp.destAPI是用来告知输出文件的路径的。一个任务可以有多个输出,如一个用来输出原来的版本(即源文件),一个输出处理后的版本(即输出文件)。你可以在上面的styles任务中看到。

建议去看gulp api文档,这样会更加清楚。

js语法检查、合并和压缩任务

1
2
3
4
5
6
7
8
9
10
11
gulp.task('scripts', function() {
return gulp.src('src/scripts/**/*.js')
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'))
.pipe(concat('main.js'))
.pipe(gulp.dest('dist/assets/js'))
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest('dist/assets/js'))
.pipe(notify({ message: 'Scripts task complete' }));
});

这里用的JSHint插件,我们使用了默认的JSHint Reporter,可能适用于大多数人,想了解更多可以去jshint官网

图片压缩任务

1
2
3
4
5
6
gulp.task('images', function() {
return gulp.src('src/images/**/*')
.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
.pipe(gulp.dest('dist/assets/img'))
.pipe(notify({ message: 'Images task complete' }));
});

这里我们只用了imagemin插件,但是可以做的更好,我们可以缓存修改过的图片,或者只对修改过的图片进行再次的压缩操作,因此我们可以使用gulp-cahce插件,因此我们需要将这行代码:

1
.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))

改成:

1
.pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true })))

此时,只有新的图片或者改变过的图片才会被压缩。

文件清理

在再次发布之前,我们最好把目标文件的文件先清理掉,然后重新构建:

1
2
3
gulp.task('clean', function(cb) {
del(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], cb)
});

默认任务

我们可以通过$ gulp启动默认任务,然后在默认任务中调用其他任务:

1
2
3
gulp.task('default', ['clean'], function() {
gulp.start('styles', 'scripts', 'images');
});

看到gulp.task里面的数组了吧?这里定义了任务的依赖,也就是说default任务依赖clean任务。在这个例子中,执行gulp.start之前会先运行clean任务。Gulp里面的任务同时进行,没有明确的顺序哪个先完成,所以我们要确保clean任务执行完之后再执行gulp.start里面的任务。

虽然不建议在执行依赖任务数组的时候使用gulp.start,但是在这里我们没有办法确保clean任务执行完毕后再执行其它任务,因此这里使用gulp.start貌似是最好的选择。

Watch任务

当文件发生变化的时候,我们可能需要重新执行任务,因此我们需要配置一个监听文件变化的任务:

1
2
3
4
5
6
7
8
9
10
11
12
gulp.task('watch', function() {

// Watch .scss files
gulp.watch('src/styles/**/*.scss', ['styles']);

// Watch .js files
gulp.watch('src/scripts/**/*.js', ['scripts']);

// Watch image files
gulp.watch('src/images/**/*', ['images']);

});

我们通过gulp.watchAPI来监听文件的变化,然后执行相关的依赖任务。现在我们可以执行$ gulp watch命令来执行我们的watch任务,监听.scss.js或者图片文件的变化执行相应的任务。

LiveReload任务

当我们代码修改的时候,Gulp也可以主动帮我们刷新页面,此时我们需要配置LiveReload服务,并修改我们的watch任务:

1
2
3
4
5
6
7
8
9
gulp.task('watch', function() {

// Create LiveReload server
livereload.listen();

// Watch any files in dist/, reload on change
gulp.watch(['dist/**']).on('change', livereload.changed);

});

要让这个任务生效,我们还需要安装并开启浏览器LiveReload插件,我们也可以手动添加代码片段

整合这些任务

把上面的这些任务综合起来,就构成了一个完整的gulpfile:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// gulpfile.js
// Load plugins
var gulp = require('gulp'),
sass = require('gulp-ruby-sass'),
autoprefixer = require('gulp-autoprefixer'),
minifycss = require('gulp-minify-css'),
jshint = require('gulp-jshint'),
uglify = require('gulp-uglify'),
imagemin = require('gulp-imagemin'),
rename = require('gulp-rename'),
concat = require('gulp-concat'),
notify = require('gulp-notify'),
cache = require('gulp-cache'),
livereload = require('gulp-livereload'),
del = require('del');

// Styles
gulp.task('styles', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'expanded', }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/styles'))
.pipe(rename({ suffix: '.min' }))
.pipe(minifycss())
.pipe(gulp.dest('dist/styles'))
.pipe(notify({ message: 'Styles task complete' }));
});

// Scripts
gulp.task('scripts', function() {
return gulp.src('src/scripts/**/*.js')
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'))
.pipe(concat('main.js'))
.pipe(gulp.dest('dist/scripts'))
.pipe(rename({ suffix: '.min' }))
.pipe(uglify())
.pipe(gulp.dest('dist/scripts'))
.pipe(notify({ message: 'Scripts task complete' }));
});

// Images
gulp.task('images', function() {
return gulp.src('src/images/**/*')
.pipe(cache(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })))
.pipe(gulp.dest('dist/images'))
.pipe(notify({ message: 'Images task complete' }));
});

// Clean
gulp.task('clean', function(cb) {
del(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], cb)
});

// Default task
gulp.task('default', ['clean'], function() {
gulp.start('styles', 'scripts', 'images');
});

// Watch
gulp.task('watch', function() {

// Watch .scss files
gulp.watch('src/styles/**/*.scss', ['styles']);

// Watch .js files
gulp.watch('src/scripts/**/*.js', ['scripts']);

// Watch image files
gulp.watch('src/images/**/*', ['images']);

// Create LiveReload server
livereload.listen();

// Watch any files in dist/, reload on change
gulp.watch(['dist/**']).on('change', livereload.changed);

});

如有任何问题,可以在下面评论。