珠峰培训

gulp实用教程(二)基础

作者:

2015-11-23 15:05:51

139

在介绍gulp API之前,我们首先来说一下gulp.js工作方式。 在gulp中,使用的是Nodejs中的stream(流),首先获取到需要的stream,然后可以通过stream的pipe()方法把流导入到你想要的地方,比如gulp的插件中, 经过插件处理后的流又可以继续导入到其他插件中,当然也可以把流写入到文件中。 所以gulp是以stream为媒介的,它不需要频繁的生成临时文件,这也是我们应用gulp的一个原因。

gulp的使用流程一般是:首先通过gulp.src()方法获取到想要处理的文件流, 然后把文件流通过pipe方法导入到gulp的插件中, 最后把经过插件处理后的流再通过pipe方法导入到gulp.dest()中, gulp.dest()方法则把流中的内容写入到文件中。例如:

var gulp = require('gulp');
gulp.src('script/src.js')         // 获取文件的流的api
    .pipe(gulp.dest('dist/dest.js')); // 写文件的api

glob语法

gulp内部使用了node-glob模块来实现其文件匹配功能。我们可以使用下面这些特殊的字符来匹配我们想要的文件:

匹配符 说明
* 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符,除非路径分隔符出现在末尾
** 匹配路径中的0个或多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。
? 匹配文件路径中的一个字符(不会匹配路径分隔符)
[...] 匹配方括号中出现的字符中的任意一个,当方括号中第一个字符为^或!时,则表示不匹配方括号中出现的其他字符中的任意一个,类似js正则表达式中的用法
!(pattern|pattern|pattern) 匹配任何与括号中给定的任一模式都不匹配的
?(pattern|pattern|pattern) 匹配括号中给定的任一模式0次或1次,类似于js正则中的(pattern|pattern|pattern)?
+(pattern|pattern|pattern) 匹配括号中给定的任一模式至少1次,类似于js正则中的(pattern|pattern|pattern)+
*(pattern|pattern|pattern) 匹配括号中给定的任一模式0次或多次,类似于js正则中的(pattern|pattern|pattern)*
@(pattern|pattern|pattern) 匹配括号中给定的任一模式1次,类似于js正则中的(pattern|pattern|pattern)

下面以例子来加深理解

  • *能匹配 a.js,x.y,abc,abc/,但不能匹配a/b.js
  • . 能匹配 a.js,style.css,a.b,x.y
  • //*.js 能匹配 a/b/c.js,x/y/z.js,不能匹配a/b.js,a/b/c/d.js
  • ** 能匹配 abc,a/b.js,a/b/c.js,x/y/z,x/y/z/a.b,能用来匹配所有的目录和文件
  • **/*.js 能匹配 foo.js,a/foo.js,a/b/foo.js,a/b/c/foo.js
  • a/**/z 能匹配 a/z,a/b/z,a/b/c/z,a/d/g/h/j/k/z
  • a/**b/z 能匹配 a/b/z,a/sb/z,但不能匹配a/x/sb/z,因为只有单**单独出现才能匹配多级目录
  • ?.js 能匹配 a.js,b.js,c.js
  • a?? 能匹配 a.b,abc,但不能匹配ab/,因为它不会匹配路径分隔符
  • [xyz].js 只能匹配 x.js,y.js,z.js,不会匹配xy.js,xyz.js等,整个中括号只代表一个字符
  • [^xyz].js 能匹配 a.js,b.js,c.js等,不能匹配x.js,y.js,z.js

1. gulp.src(globs[, options])

gulp.src()方法正是用来获取流的

globs

  • 类型: String 或 Array
  • 所要读取的 glob 或者包含 globs 的数组。
  • glob返回匹配指定模式的文件名或目录。 该函数返回一个包含有匹配文件 / 目录的数组。如果出错返回 false。

    options

options.base

类型: String , 设置输出路径以某个路径的某个组成部分为基础向后拼接。glob2base

如, 请想像一下在一个路径为 client/js/somedir 的目录中,有一个文件叫 somefile.js :

gulp.src('client/js/**/*.js') // 匹配 'client/js/somedir/somefile.js' 并且将 `base` 解析为 `client/js/`
  .pipe(minify())
  .pipe(gulp.dest('build'));  // 写入 'build/somedir/somefile.js'

gulp.src('client/js/**/*.js', { base: 'client' })
  .pipe(minify())
  .pipe(gulp.dest('build'));  // 写入 'build/js/somedir/somefile.js'

2. gulp.dest(path[, options])

path为写入文件的路径; options为一个可选的参数对象,以下为选项参数:

options.cwd

类型: String 默认值: process.cwd() 输出目录的 cwd 参数,只在所给的输出目录是相对路径时候有效。

能被 pipe 进来,并且将会写文件。如果某文件夹不存在,将会自动创建它。

var gulp = require('gulp');
var less = require('gulp-less');

/**
 * main.less
 * @color: #ffffff;
   #header {color: @color;}
 */
gulp.task('default',function(){
    gulp.src('client/main.less')
        .pipe(less())
        .pipe(gulp.dest('build',{cwd:'css'}));  //写入css目录下的build目录 
});

文件被写入的路径是以所给的相对路径根据所给的目标目录计算而来。 类似的,相对路径也可以根据所给的 base 来计算。 gulp.dest(path)生成的文件路径是我们传入的path参数后面再加上gulp.src()中有通配符开始出现的那部分路径。例如:

var gulp = require('gulp');
var minify = require('gulp-uglify');
var gulp = reruire('gulp');
//有通配符开始出现的那部分路径为 **/*.js
gulp.src('script/**/*.js')
    .pipe(gulp.dest('dist')); //最后生成的文件路径为 dist/**/*.js
//如果 **/*.js 匹配到的文件为 bootstrap/bootstrap.js ,则生成的文件路径为 dist/bootstrap/bootstrap.js

用gulp.dest()把文件流写入文件后,文件流仍然可以继续使用。

3. gulp.task(name[, deps], fn)

定义一个实现的任务(task)。

name

任务的名字,如果你需要在命令行中运行你的某些任务,那么,请不要在名字中使用空格。

deps

类型: Array 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数;

gulp.task('mytask', ['array', 'of', 'task', 'names'], function() {
  // 做一些事
});

fn

为任务函数,我们把任务要执行的代码都写在里面。当你定义一个简单的任务时,需要传入任务名字和执行函数两个属性。

gulp.task('hello', function () {
   console.log('Hello!');
});

任务顺序

可以通过任务依赖来实现。例如我想要执行one,two,three这三个任务,那我们就可以定义一个空的任务,然后把那三个任务当做这个空的任务的依赖就行了:

//只要执行default任务,就相当于把one,two,three这三个任务执行了
gulp.task('default',['one','two','three']);

如果任务相互之间没有依赖,任务就会按你书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。但是如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务。例如:

gulp.task('one',function(cb){
    //one是一个异步执行的任务
    setTimeout(function(){
        console.log('one is done')
        cb();
    },5000);
});

//two任务虽然依赖于one任务,但并不会等到one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
    console.log('two is done');
});

上面的例子中我们执行two任务时,会先执行one任务,但不会去等待one任务中的异步操作完成后再执行two任务,而是紧接着执行two任务。 所以two任务会在one任务中的异步操作完成之前就执行了。 那如果我们想等待异步任务中的异步操作完成后再执行后续的任务,该怎么做呢?

第一:在异步操作完成后执行一个回调函数来通知gulp这个异步任务已经完成,这个回调函数就是任务函数的第一个参数。

gulp.task('one',function(cb){ //cb为任务函数提供的回调,用来通知任务已经完成
  //one是一个异步执行的任务
  exec(function(){
    console.log('one is finish');
    cb();  //执行回调,表示这个异步任务已经完成
  },5000);
});

//这时two任务会在one任务中的异步操作完成后再执行
gulp.task('two',['one'],function(){
  console.log('two is finish');
});

第二:返回一个promise对象,例如

var Q = require('q');
gulp.task('one', function() {
  var deferred = Q.defer();

  // 执行异步的操作
  setTimeout(function() {
    deferred.resolve();
  }, 1);
  return deferred.promise;
});

gulp.task('two',['one'],function(){
  console.log('two is done');
});

4.gulp.watch(glob [, opts], tasks)

用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件压缩等。

gulp.watch(glob[, opts], tasks)

glob

类型: String or Array

一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。

opts

类型: Object

传给 gaze 的参数。

tasks

类型: Array

需要在文件变动后执行的一个或者多个通过 gulp.task() 创建的 task 的名字,

gulp.task('test',function(){
    console.log('test');
});

var watcher = gulp.watch('client/**/*.js', ['test']);
watcher.on('change', function(event) {
    console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

gulp.watch(glob[, opts, cb])

glob

类型: String or Array

一个 glob 字符串,或者一个包含多个 glob 字符串的数组,用来指定具体监控哪些文件的变动。

opts

类型: Object

传给 gaze 的参数。

cb(event)

类型: Function

每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息, type属性为变化的类型,可以是added,changed,deleted;path属性为发生变化的文件的路径。

gulp.watch('js/**/*.js', function(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
});

callback 会被传入一个名为 event 的对象。这个对象描述了所监控到的变动:

event.type

类型: String

发生的变动的类型:added, changed 或者 deleted。

event.path

类型: String

触发了该事件的文件的路径。

run

gulp.run()表示要执行的任务。可能会使用单个参数的形式传递多个任务。如下代码:

gulp.task('end',function(){
gulp.run('task1','task3','task2');
});

注意:任务是尽可能多的并行执行的,并且可能不会按照指定的顺序运行。

资源大全

深入理解Promise实现细节

Promise实现原理

1. 导入gulp

打开 gulpfile.js ,在这里先去创建一个对象,叫 gulp ,让它等于 require('gulp'); 这样这个 gulp 对象就拥有了 gulp 提供的所有属性还有方法了。

2. 创建源文件

mkdir app
在app下创建 index.html

3. 任务目标

然后用 gulp 的 task 方法,去创建一个任务, 这个任务要做的事就是把我们项目里的app下的index.html 这个文件,复制到一个叫 dist 的目录里面,这个 dist 目录表示的是 distribution,也就是正式发新版。

4. 编写任务

  1. 先给这个任务起个名字 ... 可以叫它 copy-html
  2. 再写一个匿名函数
  3. 先用写 return
  4. 大部分的 gulp 任务,首先要做的就是去读取要处理的文件,读取文件用的是 src 这个方法,在这个方法里,指定一下要读取的文件 index.html 找到要处理的文件以后,再用一个 pipe方法,你可以把这个 pipe 想像成是一个管道, 把文件读取进来,放到一个管道里,在这个管道里面,你可以使用 gulp 的插件去处理读取进来的文件 因为这里我们只是简单的把文件保存到一个指定的地方,所以不需要使用插件,可以使用 gulp 的 dest 这个方法,在这个方法里,指定一下文件要存储到的那个位置。 这样我们就创建好了这个把单个文件复制到指定位置的任务

5. 执行

打开命令行工具 先确定当前的位置是在项目的目录里面 然后输入 gulp+后面加上任务的名字

gulp copy-html

输入回车,会出现一些提示:

  • 使用的 gulpfile 的位置
  • 开始执行的任务
  • 还有任务结束所花的时间

代码

 var gulp = require('gulp');
 gulp.task('copy-html',function(){
     return gulp.src('app/index.html').pipe(gulp.dest('dist'));
 });

1. 导入gulp

打开 gulpfile.js ,在这里先去创建一个对象,叫 gulp ,让它等于 require('gulp'); 这样这个 gulp 对象就拥有了 gulp 提供的所有属性和方法了。

2. 创建源文件

在app下创建 app/imgs/1.png 2.png 3.jpg app/imgs/small/1.png 2.png imgs下有 1.png 2.png两个png图片,3.jpg 一个png图片 下面还有一个small文件夹,里面有1.png 2.png 两个png图片

3. 任务目标

然后用 gulp 的 task 方法,去创建一个任务, 把 imgs 目录里的文件复制到 dist 这个目录的下面

4. 编写任务

  1. 先给这个任务起个名字 ... 可以叫它 copy-imgs
  2. 再写一个匿名函数
  3. 先写 return
  4. 大部分的 gulp 任务,首先要做的就是去读取要处理的文件,读取文件用的是 src 这个方法 'app/imgs/*.png' 指app/imgs/这个目录里面的所有的.png格式的图片,这个 * 号表示的就是任何东西. *.jpg 意思就是,不管图片的文件名是什么,只要它是 .png 格式的就行.

5. 执行

打开命令行工具 先确定当前的位置是在项目的目录里面 然后输入 gulp+后面加上任务的名字

gulp copy-imgs

输入回车,会出现一些提示:

  • 使用的 gulpfile 的位置
  • 开始执行的任务
  • 还有任务结束所花的时间

完成以后,你会看到,在项目的 dist 这个目录里面,会创建一个叫 imgs 的目录,在这个目录里面,有两张图片 1.png 2.png.它们都是从项目根目录下的 imgs 这个目录里面复制过来的. 在这个目录里面,这张 jpg 格式的图片,还有 small 里面的图片,并没有复制到 dist 下面的 imgs 目录里面。因为我们写的 glob 只匹配imgs目录下的所有的.png格式的图片

6.指定多个后缀

如果你想把 imgs 目录下面的所有的图像文件都复制到 dist 下面的 imgs 目录里面,在这个 src 方法的 glob 里面,可以指定多个扩展名,现在是 *.png, 后面加上一组花括号,在这个花括号里面,指定多个扩展名.{jpg,png}注意逗号的后面不要使用空格.

多个glob

有些任务你可能需要用到多个 glob,这样我们就可以用一个数组,数组里的每个项目表示的就是用来匹配文件的 glob。 比如我们的项目里有两个目录,css 还有 js。 每个目录下面都有一些文件。 我们想创建一个任务,需要用到这两个目录里面的东西。 在这个任务里,我们就可以使用多个glob。

['app/css/*.css','app/js/*.js','app/js/*.tmp.js']

数组里有两个glob 再次执行任务 会发现glob中的文件都拷贝到dist下面去了

排除特定文件

glob 的前面,再加上一个!号,表示这是要排除掉的文件

['app/css/*.css','app/js/*.js','!app/js/*.tmp.js']

代码

var gulp = require('gulp');

/**
 * 复制图片
 */
gulp.task('copy-images',function(){
    return gulp.src('app/imgs/*.jpg').pipe(gulp.dest('dist'));
});

/**
 * 1. {} 里可以指定多个扩展名
 * 2. * 匹配所有的字符,除了路径分隔符 /
 * 3. ** 匹配所有的字符,包括路径分隔符 /
 */
gulp.task('copy-images',function(){
    return gulp.src('app/imgs/**/*.{jpg,png}').pipe(gulp.dest('dist'));
});

/**
 * 匹配多个目录 glob
 * 可以填写一个数组
 *
 */
gulp.task('copy-other',function(){
    return gulp.src(['app/css/*.css','app/js/*.js'],{base:'app'}).pipe(gulp.dest('dist'));
});

/**
 * 匹配多个目录 glob
 * ! 排除一个文件
 *
 */
gulp.task('copy-other',function(){
    return gulp.src(['app/css/*.css','app/js/*.js','!app/js/*.tmp.js'],{base:'app'}).pipe(gulp.dest('dist'));
});

1. 组合任务

在创建 gulp 任务的时候,我们可以去给任务指定它依赖的其它的任务。 比如,这里我们创建了三个任务,copy-html,copy-imgs,copy-other。 我们想再创建一个叫 build 的任务,这个任务依赖这里的这三个任务。

2. 编写

使用 gulp 的 task 这个方法去创建任务 先给这个任务起个名字,叫做 build. 再把这个方法的第二个参数设置成一个数组,这个数组里的项目就是这个任务所依赖的任务,输入一组方括号,再把它需要的三个任务,作为这个数组的三个项目. 我们可以继续去设计这个任务要作的事情,用一个匿名函数, 可以让它在控制台上输出 编译成功 ,这样在执行 build 任务的时候,会先去执行它依赖的三个任务,最后再执行它本身要做的任务。

3. 执行

保存,打开命令行工具,输入 gulp build,回车. 注意这里会同时去执行 build 需要的三个任务,并不是先执行一个,等待完成以后再去执行一个, 这些依赖的任务是同时执行的,等它们都完成以后,才会执行 build 本身要做的任务,这里就是在控制台上,输出编译成功 这几个字儿。

代码

var gulp = require('gulp');

gulp.task('copy-html',function(){
    return gulp.src('app/index.html').pipe(gulp.dest('dist'));
});

gulp.task('copy-images',function(){
    return gulp.src('app/imgs/**/*.{jpg,png}').pipe(gulp.dest('dist'));
});

/**
 * 匹配多个目录 glob
 * ! 排除一个文件
 *
 */
gulp.task('copy-other',function(){
    return gulp.src(['app/css/*.css','app/js/*.js','app/js/*.tmp.js'],{base:'app'}).pipe(gulp.dest('dist'));
});


gulp.task('default',['copy-html','copy-images','copy-other'],function(){
    console.log('全部拷贝任务执行完毕!');
});

1. 监听任务

使用 gulp 的 watch 这个方法,我们可以去监视一些文件,当这些文件发生变化的时候,立即去执行一些指定的任务

2.编写

先打开 gulpfile.js,再去创建一个任务 gulp.task,这个任务可以叫做 watch,再用一个匿名函数,在它里面,再用 gulp 的 watch 方法去做点事情。

输入 gulp.watch(),先指定一下要监视的文件,比如去监视 index.html 再把要执行的任务列表放到这里,这个任务列表要放到一个数组里,所以先输入一组方括号,要执行的任务是 copy-html. 它的意思就是,我们在执行 watch 任务的时候,去监视 index.html 这个文件的变化,当这个文件发生变化以后,就去自动执行 copy-html 这个任务。

执行

保存 回到命令行工具 先去执行一下刚才创建的 watch 这个任务 gulp watch ,这样 gulp 就会去监视文件的变化了,你会看到这个任务并没有立即结束,它会一直运行,直到我们手工退出这个任务,退出可以按一上 ctrl + C 再回到项目,打开 index.html 这个文件,随便修改一个地方,然后保存一下,回到命令行,你会看到 gulp 执行了 copy-html 这个任务,因为 index.html 这个文件发生了变化

每次保存被监视的文件,都会去执行指定的任务列表。 回到项目,打开 dist 下面的 index.html,你会看到修改之后的 index.html 已经被复制到了这个目录的下面.

代码

var gulp = require('gulp');

gulp.task('copy-html',function(){
    return gulp.src('app/index.html').pipe(gulp.dest('dist'));
});

gulp.task('copy-images',function(){
    return gulp.src('app/imgs/**/*.{jpg,png}',{base:'app'}).pipe(gulp.dest('dist'));
});

/**
 * 匹配多个目录 glob
 * ! 排除一个文件
 *
 */
gulp.task('copy-other',function(){
    return gulp.src(['app/css/*.css','app/js/*.js','app/js/*.tmp.js'],{base:'app'}).pipe(gulp.dest('dist'));
});


//在执行watch的时候会监控index.html文件的变化,发生变化后可以执行拷贝html的任务
gulp.task('default',function(){
    gulp.watch('app/index.html',['copy-html']);
    gulp.watch('app/imgs/**/*.{jpg,png}',['copy-images']);
    gulp.watch(['app/css/*.css','app/js/*.js','app/js/*.tmp.js'],['copy-other']);
});