gulp入门指南
Glup使用node.js串流(streams)让建构更快速,不须写出资料到硬盘的暂存档案/目录。如果你想了解更多有关串流–虽然不是必须的–你可以阅读这篇文章。Gulp利用来源档案当作输入,串流到一群外挂(plugins),最后取得输出的结果,并非配置每一个外挂的输入与输出–就像Grunt。让我们来看个范例,分别在Gulp及Grunt建构Sass:
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安装到全域环境下,让你可以存取gulp的CLI。接著,需要在本地端的专案进行安装。cd到你的专案根目录,执行下列指令(请先确定你有package.json档案):
|
1
|
$ npm install gulp --save-dev |
上述指令将gulp安装到本地端的专案内,并纪录于package.json内的devDependencies物件。
安装gulp外挂
接著安装一些外挂,完成下列任务:
- 编译Sass (gulp-ruby-sass)
- Autoprefixer (gulp-autoprefixer)
- 缩小化(minify)CSS (gulp-minify-css)
- JSHint (gulp-jshint)
- 拼接 (gulp-concat)
- 丑化(Uglify) (gulp-uglify)
- 图片压缩 (gulp-imagemin)
- 即时重整(LiveReload) (gulp-livereload)
- 清理档案 (gulp-clean)
- 图片快取,只有更改过得图片会进行压缩 (gulp-cache)
- 更动通知 (gulp-notify)
执行下列指令来安装这些外挂:
|
1
|
$ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-clean gulp-notify gulp-rename gulp-livereload gulp-cache --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'), clean = require('gulp-clean'), concat = require('gulp-concat'), notify = require('gulp-notify'), cache = require('gulp-cache'), livereload = require('gulp-livereload'); |
呼!看起来比Grunt有更多的事要做,对吧?Gulp外挂跟Grunt外挂有些许差异–它被设计成做一件事并且做好一件事。例如;Grunt的imagemin利用快取来避免重複压缩已经压缩好的图片。在Gulp中,这必须透过一个快取外挂来达成,当然,快取外挂也可以拿来快取其他东西。这让建构过程中增加了额外的弹性层面。蛮酷的,哼?
我们也可以像Grunt一样自动载入所有已安装的外挂,但这不在此文章目的,所以我们将维持在手动的方式。
建立任务
编译Sass,Autoprefix及缩小化
首先,我们设置编译Sass。我们将编译Sass、接著通过Autoprefixer,最后储存结果到我们的目的地。接著产生一个缩小化的.min版本、自动重新整理页面及通知任务已经完成:
|
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: 'Styles task complete' }));}); |
继续下去之前,一个小小的说明。
|
1
|
gulp.task('styles', function() { ... )}; |
这个gulp.taskAPI用来建立任务。可以透过终端机输入$ gulp styles指令来执行上述任务。
|
1
|
return gulp.src('src/styles/main.scss') |
这个gulp.srcAPI用来定义一个或多个来源档案。允许使用glob样式,例如/**/*.scss比对多个符合的档案。传回的串流(stream)让它成为非同步机制,所以在我们收到完成通知之前,确保该任务已经全部完成。
|
1
|
.pipe(sass({ style: 'expanded' })) |
使用pipe()来串流来源档案到某个外挂。外挂的选项通常在它们各自的Github页面中可以找到。上面列表中我有留下各个外挂的连结,让你方便使用。
|
1
|
.pipe(gulp.dest('dist/assets/css')); |
这个gulp.dest()API是用来设定目的路径。一个任务可以有多个目的地,一个用来输出扩展的版本,一个用来输出缩小化的版本。这个在上述的styles任务中已经有展示。
我建议阅读gulp的API文件,以了解这些函式方法。它们并不像看起来的那样可怕!
JSHint,拼接及缩小化JavaScript
希望你现在对于如何建立一个新的gulp任务有好想法。接下来,我们将设定脚本任务,包括lint、拼接及丑化:
|
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一个reporter。这裡我使用预设的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-cache外挂–稍早已经安装。我们需要额外设置才能使用这个外挂,因此修改这段程式码:
|
1
|
.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })) |
成为这段:
|
1
|
.pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true }))) |
现在只有新的或更动的图片会被压缩。乾淨俐落!
收拾乾淨
在我们进行佈署之前,清除目的地目录并重建档案是一个好主意–以防万一任何已经被删除的来源档案遗留在目的地目录之中:
|
1
2
3
4
|
gulp.task('clean', function() { return gulp.src(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], {read: false}) .pipe(clean());}); |
我们可以传入一个目录(或档案)阵列到gulp.src。因为我们不想要读取已经被删除的档案,我们可以加入read: false选项来防止gulp读取档案内容–让它快一些。
预设任务
我们可以建立一个预设任务,当只输入$ gulp指令时执行的任务,这裡执行三个我们所建立的任务:
|
1
2
3
|
gulp.task('default', ['clean'], function() { gulp.start('styles', 'scripts', 'images');}); |
注意额外传入gulp.task的阵列。这裡我们可以定义任务相依(task dependencies)。在这个范例中,gulp.start开始任务前会先执行清理(clean)任务。Gulp中所有的任务都是并行(concurrently)执行,并没有先后顺序哪个任务会先完成,所以我们需要确保clean任务在其他任务开始前完成。
注意: 透过相依任务阵列来执行clean而非gulp.start是经过考虑的,在这个情境来看是最好的选择,以确保清理任务全部完成。
看守
为了能够看守档案,并在更动发生后执行相关任务,首先需要建立一个新的任务,使用gulp.watchAPI来看守档案:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
gulp.task('watch', function() { // 看守所有.scss档 gulp.watch('src/styles/**/*.scss', ['styles']); // 看守所有.js档 gulp.watch('src/scripts/**/*.js', ['scripts']); // 看守所有图片档 gulp.watch('src/images/**/*', ['images']);}); |
透过gulp.watch指定想要看守的档案,并且透过相依任务阵列定义任务。执行$ gulp watch来开始看守档案,任何.scss、.js或图片档案一旦有了更动,便会执行相对应的任务。
即时重整(LiveReload)
Gulp也可以处理档案更动后,自动重新整理页面。我们需要修改watch任务,设置即时重整伺服器。
|
1
2
3
4
5
6
7
8
9
10
11
|
gulp.task('watch', function() { // 建立即时重整伺服器 var server = livereload(); // 看守所有位在 dist/ 目录下的档案,一旦有更动,便进行重整 gulp.watch(['dist/**']).on('change', function(file) { server.changed(file.path); });}); |
为了让这个功能有效,除了伺服器之外,还需要安装并启用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
79
80
|
// 载入外挂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'), clean = require('gulp-clean'), concat = require('gulp-concat'), notify = require('gulp-notify'), cache = require('gulp-cache'), livereload = require('gulp-livereload');// 样式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' }));});// 脚本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' }));});// 图片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' }));});// 清理gulp.task('clean', function() { return gulp.src(['dist/styles', 'dist/scripts', 'dist/images'], {read: false}) .pipe(clean());});// 预设任务gulp.task('default', ['clean'], function() { gulp.start('styles', 'scripts', 'images');});// 看手gulp.task('watch', function() { // 看守所有.scss档 gulp.watch('src/styles/**/*.scss', ['styles']); // 看守所有.js档 gulp.watch('src/scripts/**/*.js', ['scripts']); // 看守所有图片档 gulp.watch('src/images/**/*', ['images']); // 建立即时重整伺服器 var server = livereload(); // 看守所有位在 dist/ 目录下的档案,一旦有更动,便进行重整 gulp.watch(['dist/**']).on('change', function(file) { server.changed(file.path); });}); |
你也可以在gist看整个gulpfile。我也将达到相同任务的Gruntfile放在同一个gist,方便做比较。
gulp压缩整站方法(html/css/js/image)
注入package.json(此json文件需要自己在当前项目目录下创建即可--不能有注释)安装:
|
1
|
npm install gulp-uglify --save-dev |
全局安装:
|
1
|
npm install gulp-uglify -g |
整站静态文件压缩配置文件gulpfile.js(html/css/image/js; 前提是安装下列require用到的模块):
|
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
var gulp = require('gulp'),//基础库 htmlmin = require('gulp-htmlmin'),//html压缩 cssmin = require('gulp-minify-css'),//css压缩 jshint = require('gulp-jshint'),//js检查 uglify = require('gulp-uglify'),//js压缩 imagemin = require('gulp-imagemin'),//图片压缩 pngquant = require('imagemin-pngquant'),//图片深入压缩 imageminOptipng = require('imagemin-optipng'), imageminSvgo = require('imagemin-svgo'), imageminGifsicle = require('imagemin-gifsicle'), imageminJpegtran = require('imagemin-jpegtran'), domSrc = require('gulp-dom-src'), cheerio = require('gulp-cheerio'), processhtml = require('gulp-processhtml'), Replace = require('gulp-replace'), cache = require('gulp-cache'),//图片压缩缓存 clean = require('gulp-clean'),//清空文件夹 conCat = require('gulp-concat'),//文件合并 plumber=require('gulp-plumber'),//检测错误 gutil=require('gulp-util');//如果有自定义方法,会用到 var date = new Date().getTime();gulp.task('clean',function(){ return gulp.src('min/**',{read:false}) .pipe(clean());});function errrHandler( e ){ // 控制台发声,错误时beep一下 gutil.beep(); gutil.log(e); this.emit('end');}gulp.task('cleanCash', function (done) {//清除缓存 return cache.clearAll(done);}); gulp.task('htmlmin', function () { var options = { removeComments: true,//清除HTML注释 collapseWhitespace: true,//压缩HTML collapseBooleanAttributes: false,//省略布尔属性的值 <input checked="true"/> ==> <input /> removeEmptyAttributes: false,//删除所有空格作属性值 <input id="" /> ==> <input /> removeScriptTypeAttributes: true,//删除<script>的type="text/javascript" removeStyleLinkTypeAttributes: true,//删除<style>和<link>的type="text/css" minifyJS: true,//压缩页面JS minifyCSS: true//压缩页面CSS }; gulp.src(['index/*.htm','index/*.html']) .pipe(plumber({errorHandler:errrHandler})) .pipe(Replace(/_VERSION_/gi, date)) .pipe(processhtml()) .pipe(htmlmin(options)) .pipe(gulp.dest('min'));});gulp.task('cssmin', function(){ gulp.src('index/**/*.css') .pipe(conCat('css/index.min.css')) .pipe(plumber({errorHandler:errrHandler})) .pipe(cssmin({ advanced: false,//类型:Boolean 默认:true [是否开启高级优化(合并选择器等)] compatibility: 'ie7',//保留ie7及以下兼容写法 类型:String 默认:''or'*' [启用兼容模式; 'ie7':IE7兼容模式,'ie8':IE8兼容模式,'*':IE9+兼容模式] keepBreaks: false,//类型:Boolean 默认:false [是否保留换行] keepSpecialComments: '*' //保留所有特殊前缀 当你用autoprefixer生成的浏览器前缀,如果不加这个参数,有可能将会删除你的部分前缀 })) .pipe(gulp.dest('min')); });gulp.task('jsmin', function () { gulp.src(['index/js/*.js','!index/**/{text1,text2}.js']) .pipe(conCat('js/index.min.js')) .pipe(plumber({errorHandler:errrHandler})) .pipe(uglify({ mangle: {except: ['require' ,'exports' ,'module' ,'$']},//类型:Boolean 默认:true 是否修改变量名 compress: true,//类型:Boolean 默认:true 是否完全压缩 preserveComments: 'false' //保留所有注释 })) .pipe(gulp.dest('min'));}); gulp.task('imagemin', function () { gulp.src('index/**/*.{png,jpg,gif,ico}') .pipe(plumber({errorHandler:errrHandler})) .pipe(cache(imagemin({ progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片 svgoPlugins: [{removeViewBox: false}],//不要移除svg的viewbox属性 use: [pngquant(),imageminJpegtran({progressive: true}) , imageminGifsicle({interlaced: true}),imageminOptipng({optimizationLevel:3}), imageminSvgo()] //使用pngquant深度压缩png图片的imagemin插件 }))) .pipe(gulp.dest('min'));});gulp.task('default',['clean'],function(){ gulp.start('cssmin','htmlmin','jsmin','imagemin');}); |
package.json(例子) package.json详细介绍:https://docs.npmjs.com/files/package.json
(package.json npm init 命令行创建文件方法http://blog.csdn.net/liyanhui1001/article/details/44020235 )
|
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
|
{ "name": "web", "version": "1.0.0", "description": "my text", "main": "gulpfile.js", "dependencies": { "gulp": "^3.9.1", "gulp-cache": "^0.4.5", "gulp-concat": "^2.6.0", "gulp-htmlmin": "^2.0.0", "gulp-imagemin": "^3.0.1", "gulp-jshint": "^2.0.1", "gulp-minify-css": "^1.2.4", "gulp-plumber": "^1.1.0", "gulp-uglify": "^1.5.4", "gulp-util": "^3.0.7", "imagemin-pngquant": "^5.0.0", "jshint": "^2.9.2", "gulp-clean": "^0.3.2" }, "devDependencies": { "gulp-cheerio": "^0.6.2", "gulp-dom-src": "^0.2.0", "gulp-jslint": "^1.0.1", "gulp-processhtml": "^1.1.0", "gulp-rename": "^1.2.2", "gulp-replace": "^0.5.4", "gulp-webpack": "^1.5.0", "imagemin-gifsicle": "^5.1.0", "imagemin-jpegtran": "^5.0.2", "imagemin-optipng": "^5.1.1", "imagemin-svgo": "^5.1.0", "webpack": "^1.13.1" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "web" ], "author": "yl", "license": "ISC"} |
|
1
|
<br><br>devDependencies里的内容即为你安装gulp的模块插件名称和版本号! |
最后,node.js里指定到当前项目目录下输入gulp命令即可:
|
1
|
gulp default |

浙公网安备 33010602011771号