gulp
一、前言
gulp是一个适用于javascript的构建工具,能自动执行已定义的常见任务,如语法检测(jshint)、测试(mocha)、压缩(uglify)等。其相当于maven之于java。
还有一个更早出现的构建工具grunt,其也提供了gulp的功能,且功能更为强大,但其在配置及使用上更为繁琐,其实现的理念也导致其编译效率较低,新出现gulp的出现正是为了解决这些问题,在后面的使用过程中将逐渐对比两者的优劣。
二、安装
npm install -g gulp
gulp提供的是一个构建工具,要真正实现我们需要的功能,还需要下载相关的插件,如
npm install gulp-uglify --save-dev
三、使用API:
下面通过实现一个gulp的demo,来学习其API。
创建gulp文件夹,新建gulpfile.js作为gulp的启动文件,插入如下代码
var gulp = require('gulp');
gulp.task( 'default', function(){
console.log( 'this is gulp default' );
});
使用cli进入到 gulp目录,执行gulp,其会自动找到gulpfile.js并执行,打印信息如下
cc@cc:~/Work/gulp$ gulp
[07:23:15] Using gulpfile ~/Work/gulp/gulpfile.js
[07:23:15] Starting 'default'...
this is gulp default
[07:23:15] Finished 'default' after 133 μs
执行完后会自动退出,后面会有不自动退出的情况,μs是微秒单位
现在我们按一个较正规的系统目录,参照前面几章实现的项目。
cli到gulp目录下,执行 npm init,自行配置相关描述,可如下
{
"name": "gulp",
"version": "1.0.0",
"description": "this is a demo for gulp",
"main": "index.js",
"dependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "upopen.cn",
"license": "ISC"
}
gulp下新建assets/core/js/base.js,插入
function base( name ){
console.log( 'this is base, hi ' + name );
}
再新建assets/core/css/common.css,插入
body{
margin: 0;
padding: 0;
background: #afafaf;
}
a{
text-decoration: none;
}
下载基于uglify的代码压缩插件 gulp-uglify
cli执行 npm install gulp-uglify --save-dev
修改gulpfile.js
var gulp = require( 'gulp' ),
uglify = require( 'gulp-uglify' );
gulp.task( 'default', function(){
gulp.src( 'assets/core/js/base.js' )
.pipe( uglify() )
.pipe( gulp.dest( 'assets_min' ) );
});
cli执行gulp
即实现了将assets/core/js/base.js压缩并插入到新建文件夹 assets_min内,和nodejs写法很相同,且条目简单,若用grunt做同样的功能则需要做相当多的配置。
gulp的API总共有四个,我们一次就用到了其中的三个。
-
gulp.task( name[, deps], fn ):创建一个任务
name: String 任务名 deps: Array 任务依赖,其会在任务之前执行 fn: Function 任务事件
cli执行gulp时,默认会执行gulpfile.js里的default任务,若需执行其它任务,可以将其它任务作为default的deps,如
var gulp = require( 'gulp' ),
uglify = require( 'gulp-uglify' );
gulp.task( 'default', [ 'minify' ] );
gulp.task( 'minify', function(){
gulp.src( 'assets/core/js/base.js' )
.pipe( uglify() )
.pipe( gulp.dest( 'assets_min' ) );
});
若只是想单独执行minify,也可以通过cli执行 gulp minify,来显示执行指定的task
fn函数内的常见形式是 gulp.src( 'assets/core/js/base.js' ).pipe( uglify() ),读取文件.pipe( 执行压缩 ).pipe( 添加到文件夹 ),pipe是用流传递操作后的数据,下一个pipe接收到数据做操作后再向后传递,比起grunt的创建临时文件效率更高。可以通过输出src的引用查看
var stream = gulp.src( ... ).pipe( .. )
console.log( stream );
文件流的工作原理 https://github.com/substack/stream-handbook
fn 也支持异步的形式,需要在function添加实参cb,当异步返回时执行cb()即可,和mocha的回调添加实参done同理,如
var exec = require('child_process').exec;
gulp.task('jekyll', function(cb) {
exec('jekyll build', function(err) {
if (err) return cb(err);
cb();
});
});
注意task默认将以最大的并发数执行,多个task之间不会相互等待,若需序列执行,注意使用deps 及 异步情况
- gulp.src( globs[, options ] )
读取指定路径的文件,输出到piped的下一个插件中。
globs的语法 https://github.com/isaacs/node-glob
globs可以为Array / String,如上例中,可以是
'assets/core/js/base.js'
'asserts/*/*/base.js'
'asserts/*/*.js'
[ 'assets/*/*.js' ]
更多匹配规则可以参考 https://github.com/isaacs/minimatch
注意 * 的用法,/*.js,表示当前子目录的所有js文件,而/*/*.js,其N级下的子目录,即当有多层子目录时,不需要添加多层的*
options可以配置
buffer: true | false 是否以流的形式传播
read: true | false 是否可读
base: ''
其中只base作为统一路径可能被使用外,其它两个基本不会使用,后面会用到base
gulp.dest( path[, options ] )
将接收到的数据输出到path下,若path不存在,会自动创建
path: String | Function,可据fun生成路径
options: 几乎不用
cwd: String 输出当前路径
mode: 0777,用于配置权限
注:需要注意path的使用,其和gulp.src里的glob及 options里的base设置是相关,
注意上例中以下几种情况
gulp.src( 'assets/core/js/base.js' ).pipe( gulp.dest( 'assets_min' ) ); //assets_min/base.js
gulp.src( 'assets/core/**/base.js' ).pipe( gulp.dest( 'assets_min' ) ); //assets_min/js/base.js
gulp.src( 'assets/**/**/base.js' ).pipe( gulp.dest( 'assets_min' ) ); //assets_min/core/js/base.js
gulp.src( 'assets/core/**/base.js',{base: 'assets'}).pipe( gulp.dest( 'assets_min' ) ); //assets_min/core/js/base.js
可以看出path取的是 **部分,若设置了base,则取base后的部分。
实现一个更复杂些的例子来表现gulp的用法。
在assets/core/js/ 下与base.js同级新建common.js
新增语法检测插件jshint,及文件组合插件concat。
cli下执行 cnpm install gulp-jshint gulp-concat --save-dev
修改gulpfile.js
var gulp = require( 'gulp' ),
uglify = require( 'gulp-uglify' ),
jshint = require( 'gulp-jshint' ),
concat = require( 'gulp-concat' );
gulp.task( 'default', function(){
gulp.src( 'assets/core/**/**.js',{ base: 'assets' } )
.pipe( jshint() )
.pipe( uglify() )
.pipe( concat( 'all.js' ) )
.pipe( gulp.dest( function(){ return 'assets_min' } ) )
});
读取assets/core/js下的base.js、common.js --> jshint语法检测 --> uglify压缩 --> concat合并为all.js --> 添加到 assets_min 文件夹下
再讲下gulp的第四个API
gulp.watch( glob[, options ], task );
gulp.watch( glob[, options ], cb );
glob: String | Array 监听文件的路径
options:
tasks: Array 当被监听的文件变动时,需要执行task
cb: function 当文件变化时可执行的函数,
在gulpfile.js文件里追加
gulp.watch( 'assets/**/**.js', [ 'default' ] );
gulp.watch( 'assets/**/**.js', function( event ){
console.log( event );
})
cli重新执行gulp,相比之前可以看到cli下并没有自动退出,此时修改assets/core/js/base.js保存后,cli自动显示执行了default,并且输出了{ type: 'changed',path: '/home/cc/Work/gulp/assets/core/js/common.js' },即event表示文件的操作type及path。 打开assets_min下的all.js,可以看到修改后的内容也已经添加进来了。
gulp.watch本身会返回一个实例
其提供了events( change | end | error | ready | nomatch ) 及 method ( end | files | add | remove )
上面为watch追加的代码,可以修改为
var watcher =gulp.watch( 'assets/**/**.js', [ 'default' ] );
watcher.on( 'change', function( event ){
console.log( event );
});
可以达到同样的效果
四、插件Plugin
gulp作为一个构建工具,其只是提供了一个平台,可运行各款插件,以达到我们的项目需求,所以当我们学会使用gulp后,还需要了解其提供了哪些插件,甚至对没有提供的插件,我们可以自行封装。
插件列表 http://gulpjs.com/plugins/
常用的插件有
压缩CSS(gulp-minify-css)
语法检查(gulp-jshint)
文件拼接(gulp-concat)
文件压缩(gulp-uglify)
图片压缩(gulp-imagemin)
实时加载(gulp-livereload)
文件清理(gulp-clean)
更新通知(gulp-notify)
图片快取(gulp-cache)
Autoprefixer(gulp-autoprefixer)
当需要引用的插件列表较多时,可以使用gulp-load-plugins模块,代替所有插件的require,于是前面demo也可以写成
var gulp = require( 'gulp' ),
plugins = require( 'gulp-load-plugins' )();
gulp.task( 'default', function(){
gulp.src( 'assets/core/**/**.js',{ base: 'assets' } )
.pipe( plugins.jshint() )
.pipe( plugins.uglify() )
.pipe( plugins.concat( 'all.js' ) )
.pipe( gulp.dest( function(){ return 'assets_min' } ) );
});
注意各插件的命名。
五、browser-sync
再介绍一个很酷的功能browser-sync。
当我们修改html/css/js等静态资源文件时,需要刷新才能看到效果,如果做测试时填写了一堆表单,还需要重新来做,browser-sync就提供这样的功能。不过其目前只在修改css文件时做到单独加载,Html/Js 的自动刷新还是全页面的。
cli上执行 npm install browser-sync --save-dev
在当前demo下新建views/index.html,引入上例中的 css / js,如下
<!DOCTYPE html>
<html>
<head>
<link href="../assets/core/css/common.css" type="text/css" rel="stylesheet" />
</head>
<body>
this is a demo for browser-sync
<input type="text" />
<script src="../assets/core/js/common.js"></script>
</body>
</html>
在gulpfile.js后追加代码
...
var browserSync = require( 'browser-sync' );
...
gulp.task( 'browser-sync', function(){
var files = [ 'views/*.html', 'assets/**/*.js', 'assets/**/*.css' ];
browserSync.init( files, {
server: { baseDir: '' }
})
})
启动gulp,提示
cc@cc:~/Work/gulp$ gulp
[16:02:41] Using gulpfile ~/Work/gulp/gulpfile.js
[16:02:41] Starting 'browser-sync'...
[16:02:41] Finished 'browser-sync' after 35 ms
[16:02:41] Starting 'default'...
[16:02:41] Finished 'default' after 310 ms
[BS] Access URLs:
-------------------------------------
Local: http://localhost:3000
External: http://172.16.22.29:3000
-------------------------------------
UI: http://localhost:3001
UI External: http://172.16.22.29:3001
-------------------------------------
[BS] Serving files from: ./
[BS] Watching files...
浏览器自动打开 localhost:3000,显示cannot get /,补全网址 localhost:3000/views/index.html,即打开了前面定义的html,此时对该html及引用的common.js /common.css的任何保存操作,都会导致资源自动更新,其中css的修改,只会更新当前资源,不会导致页面的整体刷新。