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的修改,只會更新當前資源,不會導致頁面的整體刷新。