1. 什么是gulp
gulp
是可以自动化执行任务的工具
在平时开发的流程里面,一定有一些任务需要手工重复得执行,比如:
- 把文件从开发目录拷贝到生产目录
- 把多个 JS 或者 CSS 文件合并成一个文件
- 对JS文件和CSS进行压缩
- 把
sass
或者less
文件编译成CSS
- 压缩图像文件
- 创建一个可以实时刷新页面内容的本地服务器
只要你觉得有些动作是要重复去做的,就可以把这些动作创建成一个gulp任务
然后在指定的条件下自动执行
比如在less源文件发生改变后自动编译成css文件
2. gulp特点
- 易于使用 通过代码优于配置的策略,Gulp 让简单的任务简单,复杂的任务可管理。
- 快速构建 利用 node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。前一级的输出,直接变成后一级的输入,使得在操作上非常简单
- 高质量的插件 Gulp 严格的插件指南确保插件如你期望的那样简洁地工作。
- 易于学习 通过最少的 API,掌握 gulp 毫不费力,构建工作尽在掌握。
3. 流
3.1 流的概念
- Stream是
nodejs
各种对象实现的抽象接口。
- 所有的
stream
对象都是EventEmitter
的实例,可以发射事件。
- 流是一种有起点和终点的数据传输手段。
- 上一个的输出是下一个的输入
3.2 gulp中的流
- gulp正是通过代码优于配置的策略来尽量简化任务编写的工作。
- 类似
jquery
里的链式操作,把各个方法串连起来构建完整的任务。
- 用gulp编写任务也可看作是用
Node.js
代码编写任务。
- 当使用流时,gulp不需要生成大量的中间文件,只将最后的输出写入磁盘,整个过程因此变得非常快。
3. 安装gulp
在项目里使用gulp需要
- 安装node
- 在全局范围内去安装一下gulp的命令行工具
- 然后在项目里面再去本地安装gulp
5.1 安装node
gulp是基于Nodejs的自动任务运行器
安装Gulp和相关行件用的都是node的包管理工具npm
所以你需要先在电脑上安装 node,确定在命令行工具的下面可以使用npm这个命令,这样就能去安装Gulp了
node.js官网
安装好以后,我们可以打开命令行工具,mac 用户可以使用终端工具,windows 用户可以找到cmd命令行工具。
5.2 gulp 命令行工具
- 使用
npm install
去安装 gulp,注意加上一个 -g
的参数,表示在全局范围内去安装.
- 一般用 npm 安装的时候用一个 -g 的参数就表示,这个安装的东西会作为命令去执行。
- 如果你在mac或linux下遇到了权限问题,在下面这个命令的前面加上
sudo npm install gulp -g
并输入mac密码。
安装完成后可以输入 gulp --help
如果输出一些帮助的信息就表示可以gulp命令行成功安装了
如果安装不上可以换一下源试试
5.3 gulp本地安装
先创建一个目录
在 mac 和 linux 操作系统下创建一个文件夹
使用 cd
命令进入此目录
创建项目描述文件package.json
项目需要一个叫package.json
的文件来管理项目的配置,可以使用 npm init
这个命令生成
1 2 3 4 5 6 7 8 9 10 11 12 13
| $ npm init name: (zhufeng_automation) learngulp //项目名称 version: (1.0.0) 1.0.0 //项目版本号 description: learn gulp //项目说明 entry point: (index.js) index.js // 入口文件 test command: test.js //测试脚本 执行npm test时会执行此文件 git repository: (https://github.git) //模块的git仓库 keywords: node.js gulp //在npmjs官网搜索时的关键字 author: zhufengpeixun //项目作者名字 license: (ISC) MIT //授权协议 About to write to D:\mygit\zhufeng_automation\package.json: Is this ok? (yes) yes //直接回车确认
|
回车后会在当前目录下创建一个 package.json 文件
4. 安装gulp
- 本地安装gulp到开发依赖中
1
| $ npm install gulp --save-dev
|
这样可以把 gulp 作为项目的开发依赖(只在开发时用,不会发布到线上)
第一会在node_modules
下安装本地的gulp
库
第二会把并把添加配置到package.json
文件里面
1 2 3
| "devDependencies": { + "gulp": "^3.9.0" }
|
5. 运行gulp
5.1 创建配置文件
gulp的任务要放到一个叫 gulpfile.js
的文件里面
先在项目的根目录下面创建一个这样的文件
然后在这个文件的顶部添加下面这行代码
1
| + var gulp = require('gulp');
|
通过require
可以把gulp
模块引入当前项目并赋值给gulp
变量
这样gulp
这个变量里面就会拥有gulp的所有的方法了
5.2 创建gulp的任务
可以使用gulp的task方法
同样我们去创建一个叫 hello 的任务,它要做的事就是在控制台上输出 “您好” 这两个字
- 第一个参数是任务的名称
- 第二个参数是任务的定义,是一个匿名函数
1 2 3
| gulp.task('hello', function () { console.log('您好'); });
|
5.3 执行gulp任务
打开命令行工具,进入到项目所在的目录,然后输入:
会返回:
1 2 3
| [21:36:34] Starting 'hello'... 您好 [21:36:34] Finished 'hello' after 959 μs
|
gulp后面跟着的是任务的名称
不输入任务名称的话会默认找default
任务,找不到会报错
5.4 执行其它任务
可以使用
1
| $ gulp <task> <othertask>
|
6. gulp参数
gulp 只有你需要熟知的参数标记,其他所有的参数标记只在一些任务需要的时候使用。
- -v 或 –version 会显示全局和项目本地所安装的 gulp版本号
- –gulpfile 手动指定一个 gulpfile 的路径,这在你有很多个 gulpfile 的时候很有用。这也会将 CWD 设置到该 gulpfile 所在目录
- –cwd dirpath 手动指定 CWD。定义 gulpfile 查找的位置,此外,所有的相应的依赖(require)会从这里开始计算相对路径
- -T 或 –tasks 会显示所指定 gulpfile 的 task 依赖树
- –color 强制 gulp 和 gulp 插件显示颜色,即便没有颜色支持
- –no-color 强制不显示颜色,即便检测到有颜色支持
- –silent 禁止所有的 gulp 日志
7. gulp.js工作方式
gulp的使用流程一般是
- 首先通过gulp.src()方法获取到想要处理的文件流
- 然后把文件流通过pipe方法导入到gulp的插件中
- 最后把经过插件处理后的流再通过pipe方法导入到gulp.dest()中
- gulp.dest()方法则把流中的内容写入到文件中
1 2 3
| var gulp = require('gulp'); gulp.src('script/src.js') .pipe(gulp.dest('dist/dest.js'));
|
8. gulp核心API
gulp只有4个核心API
8.1 gulp.src()
在Gulp中,使用的是Nodejs中的stream(流),首先获取到需要的stream
然后可以通过stream的pipe
方法把流导入到你想要的地方
比如Gulp的插件中,经过插件处理后的流又可以继续导入到其他插件中,当然也可以把流写入到文件中
所以Gulp是以stream为媒介的,它不需要频繁的生成临时文件,这也是Gulp的速度快的一个原因
gulp.src()
方法正是用来获取流的
但要注意这个流里的内容不是原始的文件流,而是一个虚拟文件对象流,这个虚拟文件对象中存储着原始文件的路径、文件名和内容等信息
vinyl
1 2 3 4 5 6 7 8 9 10 11 12
| var File = require('vinyl'); var indexFile = new File({ cwd: "/", base: "/test/", path: "/test/index.js", contents: new Buffer("name=zfpx") }); console.log(File.isVinyl(indexFile)); console.log(indexFile.isBuffer()); console.log(indexFile.isStream());
|
其语法为:
1
| gulp.src(globs[, options])
|
- globs 参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组
- options 为可选参数。通常情况下我们不需要用到
8.2 glob语法
gulp内部使用了node-glob
模块来实现其文件匹配功能。我们可以使用下面这些特殊的字符来匹配我们想要的文件:
8.2.1 glob规则
匹配符 | 说明 |
| 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符 |
** | 匹配路径中的0个或多个目录及其子目录 |
? | 匹配文件路径中的一个字符(不会匹配路径分隔符) |
[…] | 匹配方括号中出现的字符中的任意一个,当方括号中第一个字符为^或!时,则表示不匹配方括号中出现的其他字符中的任意一个 |
!(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) |
8.2.2 glob示例
glob | 匹配 |
* | 能匹配 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,能用来匹配所有的目录和文件 |
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 |
8.3 gulp.dest()
是用来向硬盘写入文件的,其语法为:
1
| gulp.dest(path[,options])
|
- path 为写入文件的路径
- options 为一个可选的参数对象,通常我们不需要用到
要想使用好gulp.dest()
这个方法,就要理解给它传入的路径参数与最终生成的文件的关系。
gulp.dest()
传入的路径参数只能用来指定要生成的文件的目录,而不能指定生成文件的文件名
它生成文件的文件名使用的是导入到它的文件流自身的文件名
所以生成的文件名是由导入到它的文件流决定的
1 2 3 4
| var gulp = require('gulp'); gulp.src('script/jquery.js') .pipe(gulp.dest('dist/jquery.js'));
|
gulp.dest(path)
生成的文件路径是我们传入的path参数后面再加上gulp.src()
中有通配符开始出现的那部分路径
通过指定gulp.src()
方法配置参数中的base
属性,我们可以更灵活的来改变gulp.dest()
生成的文件路径
1 2 3 4 5
| gulp.src(script/lib //假设匹配到的文件为script/lib/jquery.js //此时生成的文件路径为 build/lib/jquery.js .pipe(gulp.dest('build'))
|
8.4 gulp.task()
gulp.task
方法用来定义任务,其语法为:
1
| gulp.task(name[, deps], fn)
|
- name 为任务名称
- deps 是当前定义的任务需要依赖的其他任务,为一个数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数
- fn 为任务定义函数,我们把任务要执行的代码都写在里面。该参数也是可选的。
1 2 3 4
| gulp.task('mytask', ['array', 'of', 'task', 'names'], function() { });
|
如果某个任务所依赖的任务是异步的,就要注意了,gulp并不会等待那个所依赖的异步任务完成,而是会接着执行后续的任务
1 2 3 4 5 6 7 8 9 10
| gulp.task('one',function(){ setTimeout(function(){ console.log('one is done') },5000); }); gulp.task('two',['one'],function(){ console.log('two is done'); });
|
在异步操作完成后执行一个回调函数来通知gulp这个异步任务已经完成,这个回调函数就是任务函数的第一个参数
1 2 3 4 5 6 7
| gulp.task('one',function(cb){ setTimeout(function(){ console.log('one is done'); cb(); },5000); });
|
8.5 gulp.watch()
用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件拷贝等。其语法为
1
| gulp.watch(glob[, opts], tasks)
|
- glob 为要监视的文件匹配模式,规则和用法与
gulp.src()
方法中的 glob 相同。
- opts 为一个可选的配置对象,通常不需要用到
- tasks 为文件变化后要执行的任务,为一个数组
1 2 3 4 5 6 7
| gulp.task('uglify',function(){ }); gulp.task('reload',function(){ }); gulp.watch('js/**/*.js', ['uglify','reload']);
|
另外一种使用方式:
1
| gulp.watch(glob[, opts, cb])
|
- glob和 opts 参数与第一种用法相同
cb
参数为一个函数。每当监视的文件发生变化时,就会调用这个函数,并且会给它传入一个对象,该对象包含了文件变化的一些信息 type
属性为变化的类型,可以是 added、changed、deleted和path属性为发生变化的文件的路径1 2 3 4 5 6
| gulp.watch('js/**/*.js', function(event){ console.log(event.type); console.log(event.path); });
|
9.基本任务
9.1 复制单个文件
1 2 3 4
| var gulp = require('gulp'); gulp.task('copy-html',function(){ return gulp.src('app/index.html').pipe(gulp.dest('dist')); });
|
9.2 复制多个文件
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
| 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')); }); * 1. 匹配多个目录 glob * 2. 可以填写一个数组 */ gulp.task('copy-other',function(){ return gulp.src(['app/css/*.css','app/js/*.js'],{base:'app'}) .pipe(gulp.dest('dist')); }); * 1. 匹配多个目录 glob * 2. !表示 排除一个文件 */ gulp.task('copy-other',function(){ return gulp.src(['app/css/*.css','app/js/*.js' ,'!app/js/*.tmp.js'],{base:'app'}) .pipe(gulp.dest('dist')); });
|
9.3 组合任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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')); }); 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('全部拷贝任务执行完毕!'); });
|
9.4 监听任务
使用 gulp 的 watch 这个方法,我们可以去监视一些文件,当这些文件发生变化的时候,立即去执行一些指定的任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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')); }); 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',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']); });
|
10. gulp插件
gulp提供了一些很实用的接口,但本身并不能做太多的事情
可以读取文件、写入文件以及监控文件等一少部分功能
其它实用的功能都是依靠插件来进行扩展的
这些插件可以实现比如
- 编译 Sass:gulp-sass
- 编译 Less:gulp-less
- 合并文件:gulp-concat
- 压缩js 文件:gulp-uglify
- 重命名js文件:gulp-rename
- 优化图像大小:gulp-imagemin
- 压缩css 文件:gulp-minify-css
- 创建本地服务器:gulp-connect
- 实时预览 gulp-connect
10.1 使用插件步骤
npm install xxx --save-dev
安装插件
- 在 gulpfile.js 顶部引入此插件
- 在创建任务的时候使用此插件
10.2 gulp-load-plugins
这个插件能自动帮你加载package.json
文件里的gulp插件。
例如假设你的package.json文件里的依赖是这样的:
1 2 3 4 5
| "devDependencies": { "gulp": "^3.9.0", "gulp-concat": "^2.6.0", "gulp-connect": "^2.2.0" }
|
然后我们可以在gulpfile.js
中使用gulp-load-plugins
来帮我们加载插件
1 2
| var gulp = require('gulp'); var $ = require('gulp-load-plugins')();
|
然后我们要使用gulp-concat
和gulp-connect
这两个插件的时候,
就可以使用$.concat
和$.connect
来代替了,也就是原始插件名去掉gulp-
前缀,之后再转换为驼峰命名。
10.3 less
less插件可以把less文件编译成css
1
| $ npm install gulp-less --save-dev
|
1 2 3 4 5 6 7 8 9 10
| var gulp = require('gulp'); var less = require('gulp-less'); gulp.task('less',function(){ return gulp.src('app/less/*.less') .pipe(less()) .pipe(gulp.dest('dist/css')); }); gulp.task('default',['less']);
|
10.4 gulp-concat
这个插件可以把几个文件合并到一块
1
| $ npm install gulp-concat --save-dev
|
1 2 3 4 5 6 7 8 9 10
| var gulp = require('gulp'); var concat = require('gulp-concat'); gulp.task('concat',function(){ return gulp.src(['app/js/*.js','!app/js/*.tmp.js']) .pipe(concat('app.js')) .pipe(gulp.dest('dist/js')); }); gulp.task('default',['concat']);
|
10.5 gulp-uglify
合并后我们可以对JS文件进行压缩,最小化处理
1
| $ npm install gulp-uglify --save-dev
|
1 2 3 4 5 6 7 8 9 10 11 12
| var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify') gulp.task('uglify',function(){ return gulp.src(['app/js/*.js','!app/js/*.tmp.js']) .pipe(concat('app.js')) .pipe(uglify()) .pipe(gulp.dest('dist/js')); }); gulp.task('default',['uglify']);
|
10.6 gulp-rename
在把处理好的文件存放到指定的位置之前,我们可以先去重新命名一下它
1
| $ npm install gulp-rename --save-dev
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var rename = require('gulp-rename'); gulp.task('uglify',function(){ return gulp.src(['app/js/*.js','!app/js/*.tmp.js']) .pipe(concat('app.js')) .pipe(gulp.dest('dist/js')) .pipe(uglify()) .pipe(rename('app.min.js')) .pipe(gulp.dest('dist/js')); }); gulp.task('default',['uglify']);
|
10.7 gulp-minify-css
压缩css
1
| $ npm install gulp-minify-css --save-dev
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var gulp = require('gulp'); var less = require('gulp-less'); var minify = require('gulp-minify-css'); var rename = require('gulp-rename'); gulp.task('minify',function(){ return gulp.src('app/less/page.less') .pipe(less()) .pipe(gulp.dest('dist/css')) .pipe(minify()) .pipe(rename('page.min.css')) .pipe(gulp.dest('dist/css')); }); gulp.task('default',['less']);
|
10.8 gulp-minify-html
html文件压缩
1
| $ npm install gulp-minify-html --save-dev
|
1 2 3 4 5 6 7 8
| var gulp = require('gulp'), minifyHtml = require("gulp-minify-html"); gulp.task('minify-html', function () { gulp.src('src/*.html') .pipe(minifyHtml()) .pipe(gulp.dest('dist/html')); });
|
10.9 gulp-imagemin
如果要想在保证不改变图像质量的情况下,让图像文件的体积变得更小一点,我们可以使用gulp-imagemin
1
| $ npm install gulp-imagemin --save-dev
|
1 2 3 4 5 6 7 8 9 10
| var gulp = require('gulp'); var imagemin = require('gulp-imagemin'); gulp.task('copy-images',function(){ return gulp.src('app/imgs/**/*.{jpg,png}') .pipe(imagemin()) .pipe(gulp.dest('dist')); }); gulp.task('default',['copy-images']);
|
10.10 gulp-connect
有些时候我们需要把文件放到本地服务器上去预览,gulp-connect可以帮我们创建一个本地服务器去运行我们的项目
1
| npm install gulp-connect --save-dev
|
1 2 3 4 5 6 7 8 9 10 11
| var gulp = require('gulp'); var connect = require('gulp-connect'); gulp.task('server',function(){ connect.server({ root:'dist', port:8080 }); }); gulp.task('default',['server']);
|
10.11 自动刷新
我们希望当文件变化的时候浏览器可以自动刷新,这样我们就不需要文件修改后手动去刷新浏览器了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| var gulp = require('gulp'); var connect = require('gulp-connect'); gulp.task('copy-html',function(){ gulp.src('app/index.html') .pipe(gulp.dest('dist')) .pipe(connect.reload()); }); gulp.task('watch',function(){ gulp.watch('app/index.html',['copy-html']); }); gulp.task('server',function(){ connect.server({ root:'dist', port:8080, livereload:true }); }); gulp.task('default',['server','watch']);
|
10.12 jshint
可以用此插件进行代码检查,注意必须同时安装jshint和gulp-jshint
全部选项
1
| npm install jshint gulp-jshint --save-dev
|
1 2 3 4 5 6 7 8
| var gulp = require('gulp'), jshint = require("gulp-jshint"); gulp.task('jsLint', function () { gulp.src('src/*.js') .pipe(jshint()) .pipe(jshint.reporter()); });
|
11 gulp.js 特点
- 使用gulp.js构建的是代码不是配置文件
- 使用node标准库编写脚本
- 插件非常简单,职责单一
- 任务都是最大的并发数执行
12 学习建议
- 多了解插件库,利用最合适的插件。
- 常用的插件,仔细阅读 文档以便更好使用。
- 学习好自身的API
- 尝试编写适合自己工作流程中和习惯的
plugin
13 项目实战
13.1 安装生成器gulp-webapp
yeoman
13.2 生成项目
1 2 3 4
| $ mkdir gulpstart $ cd gulpstart $ npm install generator-gulp-webapp@0.2.0 -g $ yo gulp-webapp gulpstart
|
会打开一个向导,在这里为了简化起见,我们不使用任何第三方库。
13.3 阅读gulpfile.js文件
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 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
| 'use strict'; var gulp = require('gulp'); * 读取package.json中的devDependencies配置项,调用gulp api把相关的插件加载进来 * 返回一个object,每个key指向各自的插件,把对象赋值给 $,后面可以用$获取每个插件的引用 * 如果不使用gulp-load-plugins,就可以使用require引入插件 如 * var sass = require('gulp-sass'); * */ var $ = require('gulp-load-plugins')(); * 1.此插件是用来编译sass的 * 2. */ gulp.task('styles', function () { return gulp.src('app/styles/main.scss') * 1.pipe是steam模块中负责传递流数据的方法,把前一个流里的数据以管道的方式传递给下一个管道。 * 2.标准的流当期中一个管道出错的时候会触发unpipe事件,会导致gulp报错并强制退出 * 我们现在监听main.scss文件的变化,一旦变化时会执行rubySass,但如果sass有语法错误,执行rubySass的时候会报错,会导致gulp报错并强制退出从而导致整个任务失败,watch就会失败,只能重启gulp服务。 * 然后不停的失败再重启,这样肯定是不可接受的。 * 3.plumber是水管工的意思,plumber通过替换pipe方法并移除onerror处理函数,这样即使有管道出问题了不会影响其它管道以及影响其它后续数据流的再处理。 * 当我们希望我们的管道能容忍容错的时候,就必须先通过plumber插件。 */ .pipe($.plumber()) * 通过 npm install gulp-ruby-sass 安装 */ .pipe($.rubySass({ style: 'expanded', precision: 10 })) .pipe($.autoprefixer({browsers: ['last 1 version']})) .pipe(gulp.dest('.tmp/styles')); }); gulp.task('jshint', function () { return gulp.src('app/scripts/**/*.js') .pipe($.jshint()) .pipe($.jshint.reporter('jshint-stylish')) .pipe($.jshint.reporter('fail')); }); gulp.task('html', ['styles'], function () { var lazypipe = require('lazypipe'); var cssChannel = lazypipe() .pipe($.csso) .pipe($.replace, 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap','fonts'); var assets = $.useref.assets({searchPath: '{.tmp,app}'}); return gulp.src('app/*.html') .pipe(assets) .pipe($.if('*.js', $.uglify())) .pipe($.if('*.css', cssChannel())) .pipe(assets.restore()) .pipe($.useref()) .pipe($.if('*.html', $.minifyHtml({conditionals: true, loose: true}))) .pipe(gulp.dest('dist')); }); gulp.task('images', function () { return gulp.src('app/images/**/*') * 为了避免图片多次压缩失真 * imagemin只负责压缩图片,不负责检查图片是否被压缩过 * 为了避免二次压缩使用$.cache,维护一份临时文件记录哪些文件已经被处理过了,处理过了则不再传递给它包装的管道。 */ .pipe($.cache($.imagemin({ progressive: true, interlaced: true }))) .pipe(gulp.dest('dist/images')); }); gulp.task('fonts', function () { return gulp.src(require('main-bower-files')().concat('app/fonts/**/*')) .pipe($.filter('**/*.{eot,svg,ttf,woff}')) .pipe($.flatten()) .pipe(gulp.dest('dist/fonts')); }); gulp.task('extras', function () { return gulp.src([ 'app/*.*', '!app/*.html', 'node_modules/apache-server-configs/dist/.htaccess' ], { dot: true }).pipe(gulp.dest('dist')); }); gulp.task('clean', require('del').bind(null, ['.tmp', 'dist'])); * 1.connect会依赖styles * 2.这是一个node扩展的http框架,可以使用中间件方便配置 */ gulp.task('connect', ['styles'], function () { var serveStatic = require('serve-static'); var serveIndex = require('serve-index'); var app = require('connect')() * 此中间件就是自动为html插入livereload脚本,通过参数传递配置项参数 */ .use(require('connect-livereload')({port: 35729})) .use(serveStatic('.tmp')) .use(serveStatic('app')) .use('/bower_components', serveStatic('bower_components')) .use(serveIndex('app')); require('http').createServer(app) .listen(9000) .on('listening', function () { console.log('Started connect web server on http://localhost:9000'); }); }); * 1. 在命令行中执行 gulp serve 就可以自动打开浏览器,然后修改app/index.html就会实时刷新浏览器 * 2.因为watch依赖connect,所以这里connect可以省略 * 3. * */ gulp.task('serve', ['connect', 'watch'], function () { require('opn')('http://localhost:9000'); }); gulp.task('wiredep', function () { var wiredep = require('wiredep').stream; gulp.src('app/styles/*.scss') .pipe(wiredep()) .pipe(gulp.dest('app/styles')); gulp.src('app/*.html') .pipe(wiredep({exclude: ['bootstrap-sass-official']})) .pipe(gulp.dest('app')); }); * 1. watch依赖connect执行 * 2. */ gulp.task('watch', ['connect'], function () { * 1.先调用livereload的listen方法启动监听,这样文件发生改动时就可以接收到变化通知了 * 原理都是一样的,即通过在本地开启一个websocket服务,检测文件变化,当文件被修改后触发livereload任务,推送消息给浏览器刷新页面。详情参考 * http://blog.csdn.net/u010373419/article/details/38184333?utm_source=tuicool */ $.livereload.listen(); gulp.watch([ 'app/*.html', '.tmp/styles/**/*.css', 'app/scripts/**/*.js', 'app/images/**/*' ]).on('change', $.livereload.changed); gulp.watch('app/styles/**/*.scss', ['styles']); gulp.watch('bower.json', ['wiredep']); }); *编译 * */ gulp.task('build', ['jshint', 'html', 'images', 'fonts', 'extras'], function () { return gulp.src('dist/**/*').pipe($.size({title: 'build', gzip: true})); }); * 组合任务 * */ gulp.task('default', ['clean'], function () { gulp.start('build'); });
|
14 自定义插件
14.1 vinyl
gulp.src中这个流里的内容不是原始的文件流,而是一个虚拟文件对象流,这个虚拟文件对象中存储着原始文件的路径、文件名和内容等信息
vinyl
1 2 3 4 5 6 7 8 9 10 11 12
| var File = require('vinyl'); var indexFile = new File({ cwd: "/", base: "/test/", path: "/test/index.js", contents: new Buffer("name=zfpx") }); console.log(File.isVinyl(indexFile)); console.log(indexFile.isBuffer()); console.log(indexFile.isStream());
|
14.2 through2
https://www.npmjs.com/package/through2
二进制流的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var through2 = require('through2'); var fs = require('fs'); fs.createReadStream('src.txt',{highWaterMark:1}) .pipe(through2(function (chunk, encoding, callback) { for (var i = 0; i < chunk.length; i++) chunk[i] = chunk[i] + 1; this.push(chunk); callback(); })).on('data', function (data) { console.log(data.toString()); }).on('end', function (data) { console.log('end'); })
|
对象方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var through2 = require('through2'); var fs = require('fs'); var all = []; fs.createReadStream('src.txt', {highWaterMark: 1}) .pipe(through2.obj(function (chunk, enc, callback) { var data = { name: chunk.toString() } this.push(data); callback(); })) .on('data', function (data) { console.log(data) }) .on('end', function () { console.log('end') })
|
14.3 buffer往头部增加内容插件
如果你的插件依赖着一个基于 buffer 处理的库,你可能会选择让你的插件以 buffer 的形式来处理 file.contents。让我们来实现一个在文件头部插入额外文本的插件:
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
| var through = require('through2'); var PluginError = require('gulp-util').PluginError; const PLUGIN_NAME = 'gulp-prefixer'; function gulpPrefixer(prefixText) { if (!prefixText) { throw new PluginError(PLUGIN_NAME, 'Missing prefix text!'); } prefixText = new Buffer(prefixText); var stream = through.obj(function(file, enc, cb) { if (file.isStream()) { this.emit('error', new PluginError(PLUGIN_NAME, 'Streams are not supported!')); return cb(); } if (file.isBuffer()) { file.contents = Buffer.concat([prefixText, file.contents]); } this.push(file); cb(); }); return stream; }; module.exports = gulpPrefixer;
|
上述的插件可以这样使用:
1 2 3 4 5 6
| var gulp = require('gulp'); var gulpPrefixer = require('gulp-prefixer'); gulp.src('files/**/*.js') .pipe(gulpPrefixer('prepended string')) .pipe(gulp.dest('modified-files'));
|
14.4 stream往头部增加内容插件
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
| var through = require('through2'); var PluginError = require('gulp-util').PluginError; const PLUGIN_NAME = 'gulp-prefixer'; function prefixStream(prefixText) { var stream = through(); stream.write(prefixText); return stream; } function gulpPrefixer(prefixText) { if (!prefixText) { throw new PluginError(PLUGIN_NAME, 'Missing prefix text!'); } prefixText = new Buffer(prefixText); var stream = through.obj(function (file, enc, cb) { if (file.isBuffer()) { this.emit('error', new PluginError(PLUGIN_NAME, 'Buffers not supported!')); return cb(); } if (file.isStream()) { var streamer = prefixStream(prefixText); streamer.on('error', this.emit.bind(this, 'error')); file.contents = file.contents.pipe(streamer); } this.push(file); cb(); }); return stream; } module.exports = gulpPrefixer;
|
上述的插件可以这样使用:
1 2 3 4 5 6
| var gulp = require('gulp'); var gulpPrefixer = require('gulp-prefixer'); gulp.src('files/**/*.js', { buffer: false }) .pipe(gulpPrefixer('prepended string')) .pipe(gulp.dest('modified-files'));
|