是時候升級你的gulp到4.0了
本文的主角是gulp,所以花一兩句話來介紹gulp還是有必要的。
gulp是一款基于 stream 的前端構建工具,由于底層使用stream,可以將多個任務無縫串連在一起,相比使用臨時文件的grunt要快不少;同時也不用像grunt一樣寫一大堆配置文件,每一個任務都可以可編程的來完全控制邏輯。
gulp比grunt“快”這是公認的事實,這里不再過多比較兩者之間的差異,還是那句話,各有千秋吧。
gulp 4.0的變化
扯完了廢話,開始進入正題。
gulp團隊大概在兩個月前提交了 4.0分支 ,新版本帶來了新的api,新api給任務流程控制帶來了“革命性”的進步。
但新版本并未提交到npm,可能現在連alpha都算不上吧,不過還是可以先進行體驗的。
安裝gulp 4.0
想體驗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傳遞三參數的用法
即這種用法將報錯
gulp . task ( 'watch' , [ 'default' ] , function ( ) { // TODO // watch file } ) ; |
在gulp4.0之前,這種用法將會保證default任務先執行完再執行watch任務,gulp的任務流程控制就是這么實現的,不過這也是老版本gulp的弱點之一。
對我們這些普通使用者來說,最大的變化有兩點
gulp.task
的變化
gulp官方建議:
- 當我們想在命令行通過敲
gulp taskname
的方式執行一個任務,這時候你應該使用gulp.task
注冊taskName
- 當一個較復雜的任務(如dist)由很多個子任務組合而成的時候,子任務使用具名函數即可,不用單獨為每個子任務進行注冊,而只需將
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的神奇之處吧。
example
以開發中最常見的dist任務為例,使用gulp首先得分解任務,dist大致分解成子任務如下
- 刪除開發目錄dev,
clean-dev
- 刪除發布目錄dist,
clean-dist
- 合圖并修改css中圖片引用,
sprite
- 預編譯css(如sass)到dev,
compile-css
- 預編譯js到dev,
compile-js
- 從src拷貝html到dev,
copy-html
- 對dev下面的js/css進行md5,再拷貝到dist,
reversion
- 替換dev下html中js/css進行過md5之后的文件路徑,并拷貝到dist,
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的開發者最好還是看看咯。