gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。
gulp是基于Nodejs的自动任务运行器, 她能自动化地完成 javascript/coffee/sass/less/html/image/css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。在实现上,她借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。通过本文,我们将学习如何使用Gulp来改变开发流程,从而使开发更加快速高效。
1. gulp使用步骤
1.1 安装Node.js
参考文章 http://www.cnblogs.com/roddy/p/6524252.html
1.2 全局安装gulp
$ npm install gulp -g
查看是否正确安装。
$ gulp -v
出现版本号表示安装成功。
1.3 安装项目依赖包gulp
我们新建一个项目文件夹"gulp",然后定位到"gulp"文件中
$ cd gulp
1.3.1 了解package.json
在安装项目依赖包gulp之前,我们先来看看package.json文件。
package.json是每个基于Nodejs项目必不可少的配置文件,其中包括项目名称、版本号和所需要的依赖包列表等信息,它是存放在项目根目录中的json文件。
想像一下,如果把项目比作是餐厅中一道菜的话,项目中使用到的依赖包就是这道菜的食材,而package.json文件就是这道菜的食材列表,那么package.json存在的作用是什么?我想最主要的作用就是方便管理项目,用过github童鞋都知道,多人协同工作的时候经常会使用pull和push,由于package.json存在,我们就不需要对项目依赖纳入版本管理,只需要管理package.json就行了,然后根据package.json本地构建项目依赖。
基于命令行创建package.json文件
$ npm init
然后根据提示输入。
package.json基本格式(注意:实际json文件中是不允许出现注释的!)
{
"name": "gulp", //项目名称(必须)
"version": "1.0.0", //项目版本(必须)
"description": "This is gulp project !", //项目描述(必须)
"homepage": "", //项目主页
"repository": { //项目资源库
"type": "git",
"url": ""
},
"author": { //项目作者信息
"name": "roddy",
"email": "aaa@qq.com"
},
"license": "ISC", //项目许可协议
"devDependencies": { //项目依赖列表
"gulp": "^3.8.11"
}
}
当然,你也可以直接在项目根目录中创建package.json文件,然后复制上面配置代码进去。OK! 现在"gulp"项目中只有一个"package.json"文件,那么我们要怎么安装项目依赖包gulp呢?其实我们"package.json"中"devDependencies"字段已经列出了"gulp"包,我们只需要根据"package.json"来安装依赖就可以了,或者用npm来安装依赖。
1.3.2 根据package.json安装gulp依赖
$ npm install
1.3.3 npm安装gulp依赖
$ npm install gulp --save-dev
细心的你会发现,我们前面已经全局安装过了gulp,这里为什么还要项目安装一次gulp?全局安装gulp是让我们能使用gulp命令行,项目安装gulp是让项目能引用gulp模块!
1.3.3 创建gulpfile.js
gulpfile.js为gulp项目的配置文件也是我们的任务文件,我们使用gulp对文件进行压缩、打包这些任务就是写在gulpfile.js文件中。
我们在"gulp"项目根目录中创建一个js文件"gulp.js",代码如下:
// 引入依赖gulp
var gulp = require( 'gulp' );
// 创建一个'default'任务
gulp.task( 'default', function() {
// 将你的默认的任务代码放在这
} );
1.3.4 运行任务
用全局gulp来运行gulpfile.js中的任务(全局安装gulp才能使用gulp命令,如果没有全局安装,这里会报错!)
$ gulp
或者执行指定的任务。
$ gulp <taskname>
默认的名为"default"的任务(task)将会被运行,在这里,这个任务并未做任何事情。
2. gulp API
好了,了解gulp大致的工作步骤后,有必要我们来学习下gulp中的API,这样你才能根据自己需求写gulpfile.js。
2.1 gulp.task( name[, deps], fn )
创建一个gulp任务。
name
: [String] 任务名称,中间不要有空格。deps
: [Array] 一个包含任务列表的数组,这些任务会在你当前任务运行之前完成。fn
: [Function] 任务的回调函数,任务具体代码。
gulp.task( "taskname", [ "task1", "task2", ... ], function() {
// 将你的任务代码放在这
} );
2.2 gulp.watch( glob[, opts], tasks )
监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个EventEmitter来发射(emit)事件。
glob
: [String/Array] 监视的文件路径或者文件路径数组。opts
: [Object] 传给依赖包gaze
的参数。tasks
: [Array] 需要在文件变动后执行的一个或者多个通过 gulp.task() 创建的 task 的名字。
gulp.task( "task1", function() { console.log( "gulp" ) } );
gulp.task( "default", function() {
gulp.watch( "xx.js", [ "task1" ] );
} );
2.3 gulp.watch( glob[, opts, cb] )
监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个EventEmitter来发射(emit)事件。
glob
: [String|Array] 监视的文件路径或者文件路径数组。opts
: [Object] 传给依赖包gaze
的参数。cb
: [Function] 每次变动需要执行的回调函数,该函数被传入一个事件对象event。
├─event.type
: [String] 变动类型,"added"、"changed"、"deleted"。
└─event.path
: [String] 触发该事件的文件路径。
gulp.task( "default", function() {
gulp.watch( "xx.js", function( event ) {
console.log( event.path + "is" + event.type );
} );
}
2.4 gulp.src( globs[, options] )
匹配要处理的源文件路径,并且返回一个stream它可以被piped到别的插件中。
globs
: [String|Array] 匹配glob模式的文件路径或者文件路径数组。
├─*
: 匹配任意文件名。 例:src/*.js
<=>src中所有js文件
。
├─**
: 匹配0个或多个文件夹。 例:src/**/*.js
<=>src中0个或多个文件夹中的js文件
。
├─{}
: 匹配多个属性。 例:src/*.{js,css,html}
<=>src中所有js,css,html文件
。
└─!
: 排除文件。 例:!src/a.js
<=>不包含src中的a文件
。options
: [Object] 三个可选配置
├─options.buffer
: [Boolean] 默认true
,设置为false,将返回file.content的流并且不缓冲文件,处理大文件时非常有用。
├─options.read
: [Boolean] 默认true
,设置false,将不执行读取文件操作,返回null。
└─options.base
: [String] 默认 glob通配符之前的路径,最后替换掉base来得到输出文件路径。
假如我们src目录中有一个文件夹js,其中有一个test.js文件
gulp.task( "default", function() {
// 匹配文件"src/js/test.js", 并且把options.base解析为"src/js"
gulp.src( "src/js/*.js" )
// 用"dist"替换掉base路径,并写入该文件"dist/test.js"
.pipe( gulp.dest( "dist" ) );
} );
gulp.task( "task1", function() {
// 匹配文件"src/js/test.js", 并且把options.base设置为"src"
gulp.src( "src/js/*.js", { base : "src" } )
// 用"dist"替换掉base路径,并写入该文件"dist/js/test.js"
.pipe( gulp.dest( "dist" ) );
} );
2.5 gulp.dest( path[, options] )
能被 pipe 进来,并且将会写文件。并且重新输出(emits)所有数据,因此你可以将它 pipe 到多个文件夹。如果某文件夹不存在,将会自动创建它。
path
: [String|Function] 文件将被写入的路径(输出目录)。也可以传入一个函数,在函数中返回相应路径。options
: [Object] 两个可选配置。
├─options.cwd
: [String] 默认"options.cwd()"
,输出目录的 cwd 参数,只在所给的输出目录是相对路径时候有效。
└─options.mode
: [String] 默认"0777"
,八进制权限字符,用以定义所有在输出目录中所创建的目录的权限。
3. 基于gulp的插件
前面介绍了"gulp"怎么安装使用以及"gulp"的API,但是"gulp"到底是怎么样对文件进行压缩、合并等操作的呢?其实"gulp"要完成这些操作,靠的就是基于它的一些功能插件,利用这些插件来对文件进行操作,下面我们就介绍几种常用的插件。
3.1 CSS文件压缩
插件 : gulp-clean-css
Github : https://github.com/jakubpawlowicz/clean-css
3.1.1 本地安装
$ npm install gulp-clean-css --save-dev
3.1.2 使用
创建gulpfile.js
var gulp = require( 'gulp' );
var cleanCss = require( 'gulp-clean-css' );
gulp.task( "default", function() {
// 读取"src"中的所有css文件
return gulp.src( "src/*.css" )
// 压缩css文件
.pipe( cleanCss() )
// 写入"dist"文件夹中
.pipe( gulp.dest( "dist" ) );
} );
运行gulpfile.js中的任务
$ gulp
3.2 静态资源增加版本号更新文件引用
插件 : gulp-rev、gulp-rev-collector
Github : https://github.com/sindresorhus/gulp-rev
Github : https://github.com/shonny-ua/gulp-rev-collector
首先静态资源增加版本号发布这些牵扯到前端工程化问题,之于前端工程化,我这里不想过多解释,这里有篇讲解前端工程化的文章链接:https://www.zhihu.com/question/20790576
基本原理就是当静态资源发生版本更新后,用户浏览器会主动放弃缓存中的静态资源,从服务器拉取最新资源加载,而那些没有更新的静态资源文件则使用浏览器缓存,这样的好处就是既能提高用户访问网站的速度提升用户体验,还能减轻网站服务器压力。
要想达到这样的目的有两种思路:
非覆盖式发布:根据静态资源内容生成hash值,然后与文件名拼接生成新的静态资源文件,最后更新页面中静态资源引用路径。
这种方法需要注意的是,先上更新后的静态资源,再上页面文件。如果先上页面文件,页面中引用的静态资源路径是添加版本号的静态资源,由于服务器中不存在该版本号的静态资源,这个时候旧版页面加载不存在的静态资源就会导致页面布局错乱甚至报错。
这种方法的缺点也非常明显,每更新就会生成一次带版本号的静态资源文件,这样冗余的静态资源过多,所以需要自己定时清理掉老版本的静态资源。目前大公司一般都是使用这一种方法对静态资源进行版本迭代。
覆盖式发布:根据文件内容生成hash值,然后对引用该静态资源文件的模板链接增加版本参数。
这种方法不管是先上页面还是静态资源,都会导致用户访问页面的时候出现问题,如果先上页面,新版页面加载旧版静态资源,导致页面布局错乱;如果先上静态资源,那些有旧版静态资源文件缓存的用户不会收到影响,但是新用户访问旧版页面加载的是新版静态资源,这样还是会导致页面布局错乱等问题,所以如果一定要使用这种方法,那么最好是在页面访问量少的时间段先上静态资源文件,再上页面,当然这种方法比第一种方法的好处就是不会生成冗余的静态资源。
var gulp = require( 'gulp' );
var rev = require( 'gulp-rev' );
var revCollector = require( 'gulp-rev-collector' );
gulp.task( "default", function() {
return gulp.src( "src/*.js" )
// 生成带hash版本号文件
.pipe( rev() )
// 保存新文件
.pipe( gulp.dest( "dist" ) )
// 生成新旧文件名对应的json文件
.pipe( rev.manifest() )
// 保存json文件
.pipe( gulp.dest( "dist" ) );
} );
参考资料:
http://www.ydcss.com/archives/424
http://www.gulpjs.com.cn/docs/getting-started/