what is gulp?
gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。
gulp是基于Nodejs的自动任务运行器,她能自动化地完成javascript/coffee/sass/less/html/image/css 等文件的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。在实现上,她借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。
由于gulp是利用Node.js中的npm进行安装的,如果有不清楚npm是神马的小伙伴可以先了解一下npm
使用gulp的步骤
首先是安装nodejs(因为gulp是基于nodejs的自动任务运行器),通过nodejs的npm全局安装和项目安装gulp,其次在项目里安装所需要的gulp插件,然后新建gulp的配置文件gulpfile.js并写好配置信息(定义gulp任务),最后通过命令提示符运行gulp任务即可。
安装nodejs -> 全局安装gulp -> 项目安装gulp以及gulp插件 -> 配置gulpfile.js -> 运行任务
gulp的安装
首先确保你已经正确安装了nodejs环境。然后以全局方式安装gulp:
说明:全局安装gulp目的是为了通过她执行gulp任务;
安装:命令提示符执行cnpm/nmp install gulp -g;
查看是否正确安装:命令提示符执行gulp -v,出现版本号即为正确安装。
全局安装gulp后,还需要在每个要使用gulp的项目中都单独安装一次。把目录切换到你的项目文件夹中,然后在命令行中执行:
cnpm/npm install gulp
为了能正常使用,我们还得本地安装gulp:cnpm/npm install gulp –save-dev
我们全局安装了gulp,项目也安装了gulp,全局安装gulp是为了执行gulp任务,本地安装gulp则是为了调用gulp插件的功能。
新建gulpfile.js文件(重要)
说明:gulpfile.js是gulp项目的配置文件,是位于项目根目录的普通js文件(其实将gulpfile.js放入其他文件夹下亦可)。
它大概是这样一个js文件:
var gulp = require('gulp'), //本地安装gulp所用到的地方
less = require('gulp-less');
//定义一个testLess任务(自定义任务名称)
gulp.task('testLess', function () {
gulp.src('src/less/index.less') //该任务针对的文件
.pipe(less()) //该任务调用的模块
.pipe(gulp.dest('src/css')); //将会在src/css下生成index.css
});
gulp.task('default',['testLess', 'elseTask']);
//定义默认任务 elseTask为其他任务,该示例没有定义elseTask任务
下面一些小的解释:
1) 导入工具包 require('node_modules里对应模块')
2) gulp.task(name[, deps], fn) 定义任务 name:任务名称 deps:依赖任务名称 fn:回调函数
3) gulp.src(globs[, options]) 执行任务处理的文件 globs:处理的文件路径(字符串或者字符串数组)
4) gulp.dest(path[, options]) 处理完后文件生成路径
gulp的工作方式
Gulp是一个基于流的构建工具,因此十分灵活,其API也十分简单,就4个方法 src, dest, task, watch,分别用于输入数据,输出数据,定义任务,监控文件的变化并执行指定的任务。
Gulp本身只负责初始输入和最终输出,并提供了一个框架来管理任务,其实就连输入输出都可以不用Gulp来完成,这时候它就纯粹相当于一个任务管理的角色。
在输入输出之间的各种具体任务都是通过第三方或者用户自定义的流处理工具来完成的。
接触过Node.js的小伙伴肯定知道stream,但是再次还是再次提一下stream是个啥。
stream是Node.js提供的又一个仅在服务区端可用的模块,目的是支持“流”这种数据结构。什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时,就可以从某个地方(例如自来水厂)源源不断地到达另一个地方(比如你家的洗手池)。流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位。
有些流用来读取数据,比如从文件读取数据时,可以打开一个文件流,然后从文件流中不断地读取数据。有些流用来写入数据,比如向文件写入数据时,只需要把数据不断地往文件流中写进去就可以了。
在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了:data事件表示流的数据已经可以读取了,end事件表示这个流已经到末尾了,没有数据可以读取了,error事件表示出错了.
pipe:
就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。一个Readable流和一个Writable流串起来后,所有的数据自动从Readable流进入Writable流,这种操作叫pipe。在Node.js中,Readable流有一个pipe()方法,就是用来干这件事
gulpI介绍
1)gulp.src()
gulp.src(globs[, options])
输出(Emits)符合所提供的匹配模式(glob)或者匹配模式的数组(array of globs)的文件。 将返回一个 Vinyl files 的 stream,它可以被 piped 到别的插件中。
gulp.src('client/templates/*.jade')
pipe(jade())
.pipe(minify())
.pipe(gulp.dest('build/minified_templates'));
globs参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组。
options为可选参数。通常情况下我们不需要用到。
Gulp使用的是ejs中的stream(流),首先获取到需要的stream,然后可以通过stream的pipe()方法把流导入到你想要的地方,比如Gulp的插件中,经过插件处理后的流又可以继续导入到其他插件中,当然也可以把流写入到文件中,Gulp是以stream为媒介的,它不需要频繁的生成临时文件,这也是Gulp的速度比Grunt快的一个原因。(Grunt主要是以文件为媒介来运行它的工作流的,比如在Grunt中执行完一项任务后,会把结果写入到一个临时文件中,然后可以在这个临时文件内容的基础上执行其它任务,执行完成后又把结果写入到临时文件中,然后又以这个为基础继续执行其它任务…就这样反复下去。)再回到正题上来,gulp.src()方法正是用来获取流的,但要注意这个流里的内容不是原始的文件流,而是一个虚拟文件对象流(Vinyl files),这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息,这个我们暂时不用去深入理解,你只需简单的理解可以用这个方法来**读取你需要操件就行了
2)gulp.dest()
gulp.dest (path[,options])
能被 pipe 进来,并且将会写文件。并且重新输出所有数据,因此你可以将它 pipe 到多个文件夹。如果某文件夹不存在,将会自动创建它。
var gulp = requir('gulp');
gulp.sc('script/jquery.js')
.pipe(gulp.dest('dist/foo.js'));
//最终生成的文件路径为 dist/foo.js/jquey.js,而不是dist/foo.js
path
类型: String or Function
文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径,这个函数也可以由 vinyl 文件实例 来提供。而你输出的文件名就是你之前导入的文件流文件名。
options:为一个可选的参数对象,通常我们不需要用到。
用gulp.dest()把文件流写入文件后,文件流仍可以继续使用。
3)gulp.task()
gulp.task(name[, deps], fn)
定义一个使用 Orchestrator 实现的任务(task)。
gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
// 做一些事
});
name:任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。
deps:
类型: Array
当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数。
fn:
该函数定义任务所要执行的一些操作。
例如,我想要执行one,two,three这三个任务,那我们就可以定义一个空的任务,然后把那三个任务当做这个空的任务的依赖就行了:
//只要执行default任务,就相当于把one,two,three这三个任务执行了
gulp.task('default',['one','two','three']);
再举个例子,假如我现在希望在执行任务two之前执行one,按道理我们只需要这样:
gulp.tast('two',['one']);
那么问题来了,当你在执行任务two的时候,你的任务是否在前置依赖(这里就是任务one)之前运行了呢?
如果任务相互之间没有依赖,任务会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。但是如果某个任务所依赖的任务是异步的,就要注意了,请一定要确保你所依赖的任务列表中的任务都使用了正确的异步执行方式:使用一个 callback,或者返回一个 promise 或 stream。**因为, 默认的,task 将以最大的并发数执行,也就是说,gu且不做任何等待。gulp 会一次性运行所有的 task 并且不做:任何等待。
例如:
gulp.task('one',function(){
setTimeout(function(){
console.log('one');
},5000);
});
gulp.task('two',['one'],function(){
console.log('two');
});
我们执行two任务时,会先执行one任务,但不会去等待one任务中的异步操作完成后再执行two任务,而是紧接着执行two任务。所以two任务会在one任务中的异步操作完成之前就执行了。那如果我们想等待异步任务中的异步操作完成后再执行后续的任务,该怎么做呢?
有三种方法可以实现:
第一:在异步操作完成后执行一个回调函数来通知gulp这个异步任务已经完成,这个回调函数就是任务函数的第一个参数。
gulp.task('one',function(cb){
//cb为任务函数提供的回调,用来通知任务已经完成
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done');
cb(); //执行回调,表示这个异步任务已经完成
},5000);
});
//这时two任务会在one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
console.l
og('two is done');
});
第二:定义任务时返回一个流对象。适用于任务就是操作gulp.src获取到的流的情况。
gulp.task('one',function(cb){
var stream = gulp.src('client/**/*.js')
.pipe(dosomething()) //dosomething()中有某些异步操作
.pipe(gulp.dest('build'));
return stream;
});
gulp.task('two',['one'],function(){
console.log('two is done');
});
第三:返回一个promise对象,例如
gulp.task('one',function(cb){
var deferred = Q.defer();
// 做一些异步操作
setTimeout(function() {
deferred.resolve();
}, 5000);
return deferred.promise;
});
gulp.task('two',['one'],function(){
console.log('two is done');
});
4)gulp.watch()
gulp.watch (glob[, opts], tasks)
用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件压缩等。
glob :要监视的文件匹配模式,规则和用法与gulp.src()方法中的glob相同。
opts :为一个可选的配置对象,通常不需要用到。
tasks :为文件变化后要执行的任务,为一个数组
//do something
});
gulp.task('reload',function(){
//do something
});
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});
gulp.watch()还有另外一种使用方式:
gulp.watch(glob[, opts, cb])
glob和opts参数与第一种用法相同;
cb(event):为一个函数。
每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息,type属性为变化的类型,可以是added,changed,deleted;path属性为发生变化的文件的路径。
var watcher = gulp.watch('js/**/*.js', ['uglify','reload']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});