隨著前端工程化的推進,相信越來越多的項目都用上了自動化構建。對前端構建來說,使用最多的莫過於 grunt 和 gulp 。
本文的主角是gulp,所以花一兩句話來介紹gulp還是有必要的。
gulp是一款基於 stream 的前端構建工具,由於底層使用stream,可以將多個任務無縫串連在一起,相比使用臨時文件的grunt要快不少;同時也不用像grunt一樣寫一大堆配置文件,每一個任務都可以可編程的來完全控制邏輯。
gulp比grunt“快”這是公認的事實,這裡不再過多比較兩者之間的差異,還是那句話,各有千秋吧。
扯完了廢話,開始進入正題。
gulp團隊大概在兩個月前提交了 4.0分支 ,新版本帶來了新的api,新api給任務流程控制帶來了“革命性”的進步。
但新版本並未提交到npm,可能現在連alpha都算不上吧,不過還是可以先進行體驗的。
想體驗4.0只有通過github安裝,執行以下兩條命令即可在本地暢爽地使用gulp 4.0了。
npm install gulpjs/gulp#4.0 -g
npm install gulpjs/gulp#4.0 --save-dev
gulp 4.0相對以前的版本發生了不少變化
bach
,替換了老版本使用的 orchestrator
也許會更快些?實際上gulp已經很快了,除非是超大型項目,否則幾乎不用擔心gulp構建會花太多時間,不過尋求更快總是好的。
即這種用法將報錯
gulp . task ( 'watch' , [ 'default' ] , function ( ) {
// TODO
// watch file
} ) ;
在gulp4.0之前,這種用法將會保證default任務先執行完再執行watch任務,gulp的任務流程控制就是這麼實現的,不過這也是老版本gulp的弱點之一。
對我們這些普通使用者來說,最大的變化有兩點
gulp.task
的變化gulp官方建議:
gulp taskname
的方式執行一個任務,這時候你應該使用 gulp.task
注冊 taskName
dist
使用 gulp.task
進行注冊,以前的版本則必須將每一個子任務都先使用 gulp.task
進行注冊,然後再組合出 dist
,詳細用法見最後的例子。gulp.task又增加了一種用法,即傳遞一個具名函數作為參數,將自動注冊以該函數名命名的任務
function compile ( ) {
// TODO
gulp . src ( './src/*.js' )
. pipe ( uglify ( ) )
. pipe ( gulp . dest ( './dist/js' ) )
}
gulp . task ( compile ) ;
等同於
gulp . task ( 'compile' , function ( ) {
// TODO
gulp . src ( './src/*.js' )
. pipe ( uglify ( ) )
. pipe ( gulp . dest ( './dist/js' ) )
} ) ;
兩者都可以通過命令行運行 gulp compile
執行任務
gulp.series
和 gulp.parallel
哈哈,解放軍來了。
如果你是gulp深度使用者,你一定不止一次吐槽過gulp的任務流程難以控制,就像一條復雜的電路一樣,電路上很多電阻都是串聯加並聯的方式連接在一起,gulp一個復雜的任務同樣也是由很多個子任務以串聯(同步)加並聯(異步)的方式連接在一起的。
老版本的gulp對多個異步任務很難控制,必須借助於第三方模塊,如 run-sequence
、 event-stream
等,效果也並不理想。
現在gulp帶來了兩個新的api: gulp.series
和 gulp.parallel
,這兩個革命性的api將幫助開發者解決惱人的任務流程控制問題。
下面就來見識新api的神奇之處吧。
以開發中最常見的dist任務為例,使用gulp首先得分解任務,dist大致分解成子任務如下
clean-dev
clean-dist
sprite
compile-css
compile-js
copy-html
reversion
replcae
這只是一個普通的dist任務,我將dist拆得比較細並省略了壓縮合並等常規任務,大致由以上8個步驟組成。
拆的粒度完全由自己控制,達到方便復用又便於理解的目的就行。
使用老版本的gulp,首先需要對每一個任務進行注冊,這裡只是為了說明問題,我省略了任務的具體代碼。
gulp . task ( 'clean-dev' , function ( ) { // TODO});
gulp . task ( 'clean-dist' , function ( ) { // TODO});
gulp . task ( 'sprite' , function ( ) { // TODO});
gulp . task ( 'compile-css' , function ( ) { // TODO});
gulp . task ( 'compile-js' , function ( ) { // TODO});
gulp . task ( 'copy-html' , function ( ) { // TODO});
gulp . task ( 'reversion' , function ( ) { // TODO});
gulp . task ( 'replcae' , function ( ) { // TODO});
然後,我們來理一理任務的流程,為了讓任務執行效率更高,盡量保證能同時執行的都同時執行,這裡簡單畫了個流程圖來表示任務的流程,箭頭表示先後順序。
可以看到圖中既存在同步又存在異步的任務,需要實現這樣的流程,我們還需要修改和注冊額外的幾個任務,並借助run-sequence等第三方模塊。
gulp . task ( 'compile-css' , [ 'sprite' ] ) ;
gulp . task ( 'dev' , [ 'clean-dev' ] , function ( ) {
runSecquence ( [ 'compile-css' , 'compile-js' , 'copy-html' ] ) ;
} ) ;
gulp . task ( 'md5' , [ 'dev' , 'clean-dist' ] , function ( ) {
runSecquence ( 'reversion' ) ;
} ) ;
gulp . task ( 'dist' , [ 'md5' ] , function ( ) {
runSecquence ( 'replcae' ) ;
} ) ;
gulp官方推薦將任務最小化,每一個任務只做一件明確的事,可以看到任務拆得越細需要注冊的任務就越多,為了處理同時涉及到同步和異步的任務,需要引進額外的中間任務來銜接,在代碼上也不夠自然。
如果使用gulp 4.0,只用這樣就行了
function cleanDev ( ) { // TODO}
function cleanDist ( ) { // TODO}
function sprite ( ) { // TODO}
function compileCss ( ) { // TODO}
function compileJs ( ) { // TODO}
function copyHtml ( ) { // TODO}
function reversion ( ) { // TODO}
function replcae ( ) { // TODO}
gulp . task ( 'dist' , gulp . series (
gulp . parallel (
gulp . series (
cleanDev ,
gulp . parallel (
gulp . series (
sprite ,
compileCss
) ,
compileJs ,
copyHtml
)
) ,
cleanDist
) ,
reversion ,
replcae
) ) ;
gulp.series
和 gulp.parallel
都可以接受以 gulp.task
注冊的任務名干脆就是一個(多個)函數,省去了一大堆gulp.task的代碼,同時也達到了任務復用的目的,將子任務經過不同的組合又可以產生新的任務。
結合流程圖,上面的代碼還是很好理解的。
另外再說一點,只要在gulpfile.js中沒有使用gulp.task傳三個參數的用法,gulp 4.0也是兼容老版本的gulpfile.js的。
官方升級日志 中也列出了一些其他的說明,想升級到4.0又想完全兼容老版本gulpfile.js的開發者最好還是看看咯。