在寫(xiě)generator-aio-angular的過(guò)程中,gulp這一塊發(fā)現(xiàn)了很多非常實(shí)用的插件,大大的增加了能自動(dòng)化的范圍,這篇文章就分門(mén)別類(lèi)的簡(jiǎn)單介紹下常用的gulp插件吧。
這個(gè)分類(lèi)下主要介紹一些輔助工具類(lèi)的插件。
顧名思義,本插件的功能就是幫你自動(dòng)require你在package.json
中聲明的依賴(lài)。只要一句var $ = require('gulp-load-plugins')()
,則package.json
中聲明的gulp-
或gulp.
開(kāi)頭的插件就會(huì)自動(dòng)被放在變量$
下面。如$.util
就等于require('gulp-util')
,而有兩個(gè)連字符的插件則會(huì)自動(dòng)命名為駝峰格式,如$.taskListing
則等于require('gulp-task-listing')
。有了這個(gè)插件,就不用一個(gè)一個(gè)的require了。這個(gè)插件還有一些常用的參數(shù)配置,這里列幾個(gè)常用的:
lazyload: true
,用到這個(gè)插件的時(shí)候再去require,默認(rèn)為true。rename: {'gulp-task-listing': 'list'}
,如果有些插件名字太長(zhǎng),可以使用該參數(shù)重命名。scope: ['dependencies']
,本插件默認(rèn)會(huì)掃描package.json
里的所有dependence,可以使用該參數(shù)進(jìn)行限制。要使用這些參數(shù)只要在require的時(shí)候傳入即可,如require('gulp-load-plugins')({lazy: true})
。
這個(gè)插件的作用也很容易猜,它可以打印出gulpfile.js
中定義的所有task,這個(gè)插件我們?cè)?a target="_blank" style="background-color: transparent; color: rgb(34, 34, 34); border-bottom: 1px solid rgb(153, 153, 153);">重構(gòu)你的gulpfile這篇文章的最后介紹過(guò),值得一提的是它還可以根據(jù)task的名字確定它是不是一個(gè)子task,比如帶有:
、-
、_
的task就被認(rèn)為是子task。我一般把這個(gè)插件作為默認(rèn)的task來(lái)調(diào)用,如
|
|
這樣,如果只執(zhí)行gulp
的話(huà)就會(huì)打印出所有定義好的task,非常實(shí)用。
嚴(yán)格的說(shuō),yargs
不是專(zhuān)門(mén)用于gulp的,它是Node中處理命令行參數(shù)的通用解決方案。只要一句代碼var args = require('yargs').argv;
就可以讓命令行的參數(shù)都放在變量args
上,非常方便。它可以處理的參數(shù)類(lèi)型也是多種多樣的:
-m=5
或-m 5
,則可得到args.m = 5
。--test=5
或--test 5
,則可得到args.test = 5
。--mock
,則會(huì)被認(rèn)為是布爾類(lèi)型的參數(shù),可得到args.mock = true
。除此之外,還支持很多其他類(lèi)型的傳參方式,具體可參考它的文檔。
gulp-util帶有很多方便的函數(shù),其中最常用的應(yīng)該就是log了。$.util.log()
支持傳入多個(gè)參數(shù),打印結(jié)果會(huì)將多個(gè)參數(shù)用空格連接起來(lái)。它與console.log
的區(qū)別就是所有$.util.log
的結(jié)果會(huì)自動(dòng)帶上時(shí)間前綴。另外,它還支持顏色,如$.util.log($.util.colors.magenta('123'));
打印出來(lái)的123是品紅色的。其實(shí)$.util.colors
就是一個(gè)chalk的實(shí)例,而chalk是專(zhuān)門(mén)用來(lái)處理命令行打印著色的一個(gè)工具。
grunt自身提供一個(gè)grunt-contrib-clean用來(lái)處理支持glob匹配的刪除,而del就是gulp上對(duì)應(yīng)的工具。del支持和gulp.src
參數(shù)同樣的匹配,除此之外,它的第二個(gè)參數(shù)還支持一個(gè)回調(diào)函數(shù),當(dāng)刪除完成以后執(zhí)行,所以這是一個(gè)異步的刪除。常用的調(diào)用方法為:del([xxx], callback)
。
這是一個(gè)統(tǒng)計(jì)文件大小變化的工具,通常與壓縮類(lèi)工具放在一起實(shí)用,比如
|
|
在壓縮的pipe前后加上$.bytediff.start()
和$.bytediff.stop(callback)
,即可統(tǒng)計(jì)壓縮前后文件的變化。在callback中傳入的參數(shù)data上,可以訪問(wèn)到很多變量,如文件名,變化前后的大小,變化百分比等等。
這個(gè)插件的作用很簡(jiǎn)單,打印出stream里面的所有文件名,通常調(diào)試的時(shí)候比較需要。
這個(gè)插件也可以顧名思義:用來(lái)升級(jí)版本用的,廢話(huà)不說(shuō),直接看例子吧:
|
|
重點(diǎn)來(lái)看這里的options,我們可直接傳遞一個(gè)具體的version進(jìn)去,也可以按照Node的版本規(guī)范傳遞一個(gè)type進(jìn)去,讓其自己生成對(duì)應(yīng)的version:
version
,直接傳遞要升級(jí)到的版本號(hào),如1.2.3
。type
,可接受的值包括下面四個(gè),倘若現(xiàn)在的版本號(hào)為1.2.3
,則對(duì)應(yīng)的新版本號(hào)為:1.2.3-0
1.2.4
1.3.0
2.0.0
最終這個(gè)升級(jí)后的版本號(hào)會(huì)反映在package.json
中,當(dāng)然,你也可以在gulp.src中傳入更多的文件(如bower.json
)來(lái)替換更多的文件。
這個(gè)工具用來(lái)在壓縮后的JS、CSS文件中添加頭部注釋?zhuān)憧梢园我庀胍男畔?,通常就是作者、描述、版本?hào)、license等,比如:
|
|
這個(gè)函數(shù)將package.json
中的各種信息提取出來(lái),變成頭部注釋?zhuān)灰趬嚎s的pipe中調(diào)用.pipe(getHeader())
即可。
這個(gè)部分主要介紹一些跟stream操作有關(guān)的插件。
gulp-filter可以把stream里的文件根據(jù)一定的規(guī)則進(jìn)行篩選過(guò)濾。比如gulp.src
中傳入匹配符匹配了很多文件,可以把這些文件pipe給gulp-filter作二次篩選,如gulp.src('**/*.js').pipe($.filter(**/a/*.js))
,本來(lái)選中了所有子文件下的js文件,經(jīng)過(guò)篩選后變成名為a的子文件夾下的js文件。那有人要問(wèn)了,為什么不直接將需要的篩選傳入gulp.src
,干嘛要多篩選一步呢?這里面有兩種情況:
gulp.src
與$.filter
中間可能需要?jiǎng)e的處理,比如我對(duì)所有文件做了操作1以后,還需要篩選出一部分做操作2。
|
|
可以看到,如果沒(méi)有restore這個(gè)操作,那么拷貝到最終位置的文件將只包含被過(guò)濾出來(lái)的文件,這樣一restore,所有的文件都被拷貝了。
gulp-flatten非常實(shí)用,可能知道別的庫(kù)中flatten函數(shù)的同學(xué)已經(jīng)猜到它是干嘛的了。比如gulp.src('**/*.js')
匹配了很多文件,包括a/b/c.js
,d/e.js
,f/g/h/i/j/k.js
,l.js
,這些文件的層級(jí)都不一樣,一旦我們將這個(gè)文件pipe給$.flatten()
,則所有的文件夾層級(jí)都會(huì)去掉,最終的文件將是c.js
,e.js
,k.js
,l.js
,在一些場(chǎng)景下還是非常有用的。
這個(gè)插件的作用簡(jiǎn)單來(lái)說(shuō)就是一旦pipe中的某一steam報(bào)錯(cuò)了,保證下面的steam還繼續(xù)執(zhí)行。因?yàn)閜ipe默認(rèn)的onerror函數(shù)會(huì)把剩下的stream給unpipe掉,這個(gè)插件阻止了這種行為。那它一般用于哪種場(chǎng)景呢?比如,代碼每次build之前要跑一遍jshint和jscs來(lái)確保所有代碼都符合規(guī)范,但一旦某些代碼不符合規(guī)范,整個(gè)build流程就會(huì)停止,這個(gè)時(shí)候就需要gulp-plumber出場(chǎng)了。如:
|
|
這樣,一旦jshint或jscs報(bào)錯(cuò),整個(gè)build流程還是可以繼續(xù)走下去的,而且gulp-plumber會(huì)給出一個(gè)報(bào)錯(cuò)提醒我們:
|
|
這個(gè)插件的功能也很簡(jiǎn)單,可以條件性的添加stream,如.pipe($.if(flag, action1()))
,則只會(huì)在flag
變量為true時(shí)才會(huì)將action1()
添加到stream中去。其實(shí)不用這個(gè)插件也可以達(dá)到類(lèi)似的效果,那就是gulp-util里有一個(gè)函數(shù)叫做noop()
,也就是no operation,這個(gè)函數(shù)其實(shí)是返回一個(gè)什么都不干的空stream。利用這個(gè)函數(shù)我們可以這么寫(xiě):.pipe(flag ? action1() : $.util.noop())
,與上例的效果是一樣的。
一個(gè)gulp的task只能返回一個(gè)stream,但有的時(shí)候有這么一種情景:有兩類(lèi)文件,它們的原始位置和處理后的位置都是不同的,但它們的處理流程相同。由于gulp.src
和gulp.dest
的參數(shù)不同,我們就需要寫(xiě)兩個(gè)task來(lái)分別完成這個(gè)任務(wù),一方面略顯重復(fù),另一方面邏輯上來(lái)講這兩個(gè)task本來(lái)就是處理同樣的事情的。這種情況就需要merge-stream登場(chǎng)了,它的作用就是將多個(gè)stream合成一個(gè)返回。比如下面這個(gè)例子:
|
|
可以看到,處理的流程被提取出來(lái)放入一個(gè)函數(shù),它接受兩個(gè)參數(shù),分別是src和dest。然后在task中直接調(diào)用這個(gè)函數(shù)生成兩個(gè)stream,然后返回merge-stream合并后的結(jié)果。
gulp里的task都是異步并發(fā)執(zhí)行的,有的時(shí)候我們需要一連串的task按順序執(zhí)行,這時(shí)就需要run-sequence登場(chǎng)了。它的調(diào)用很簡(jiǎn)單:runSequence('task1', 'task2', ['task3', 'task4'], 'task5')
,這里的task都是gulp定義好的task名稱(chēng),task1完成后才會(huì)執(zhí)行task2,以此類(lèi)推。注意到task3和task4被放在中括號(hào)里了,這表明,task3和task4可以并發(fā)執(zhí)行的,但兩個(gè)都執(zhí)行完后才會(huì)執(zhí)行task5。這里要說(shuō)明的是,每個(gè)task要么返回一個(gè)stream,即return gulp.src().pipe().pipe()
,要么支持回調(diào)函數(shù),即gulp.task('task1', function (done) { action1(done); })
,滿(mǎn)足了這兩點(diǎn)才能保證正常的執(zhí)行順序,因?yàn)檫@是gulp對(duì)異步task的基本要求。
這個(gè)部分主要介紹一些將JS/CSS自動(dòng)插入到HTML的相關(guān)插件。
wiredep就是wire dependence的意思,它的作用就是把bower.json
中聲明的dependence自動(dòng)的包含到HTML中去。要插入文件,wiredep需要解決兩個(gè)問(wèn)題:
|
|
不同類(lèi)型的文件被插入到不同的區(qū)塊。
bower.json
,每個(gè)bower安裝的依賴(lài)庫(kù),根目錄下邊都有一個(gè)自己的bower.json
文件,其中的main
字段指明了使用這個(gè)庫(kù)需要包含的文件,wiredep最終包含的文件列表就來(lái)自這個(gè)字段。有些情況下,庫(kù)自身的bower.json
的main字段可能會(huì)多包含文件或少包含文件,如果想要定制這個(gè)列表,則可以在自己的bower.json
中使用overrides
字段,如下面的代碼覆蓋了mdi
這個(gè)庫(kù)的main
字段。
|
|
wiredep插件支持很多參數(shù),常用的主要有兩個(gè):
bower.json
的內(nèi)容,注意這個(gè)字段不是bower.json
文件的位置,這個(gè)參數(shù)需要使用require后的結(jié)果賦值:require('bower.json')
。使用wiredep也比較簡(jiǎn)單,直接把它傳入到stream中即可,如gulp.src('index.html').pipe(wiredep(options))
。
這個(gè)插件的作用與wiredep類(lèi)似,不同的是可以自己任意指定需要插入文件的列表。它同樣是利用注釋來(lái)尋找插入的位置,它識(shí)別的默認(rèn)注釋為<!-- inject:js -->
,但更加智能:
gulp.src
指定的源文件自動(dòng)識(shí)別注釋和插入內(nèi)容,除了支持HTML外,還支持jade、haml等。若源為jade文件,則識(shí)別的注釋為//- inject:js
,插入的內(nèi)容為:script(src="<filename>.js")
。<!-- name:ext -->
,這里的name默認(rèn)值就是“inject”,而ext的默認(rèn)值是要插入的文件的擴(kuò)展名。那么name屬性可配置意味著可以添加自定義的插入?yún)^(qū)塊,如<!-- production:js -->
,這個(gè)標(biāo)簽可以只插入生產(chǎn)環(huán)境需要包含的JS文件。這個(gè)插件的使用場(chǎng)景通常是,我們需要index里有多個(gè)區(qū)塊,比如上面name的例子,只有當(dāng)為production環(huán)境編譯的時(shí)候才去包含相關(guān)的文件。
這三個(gè)工具之所以放在一起講,是因?yàn)樗鼈円话愣际且黄鹗褂玫摹K鼈円鉀Q什么問(wèn)題呢?通過(guò)上面的wiredep也好,gulp-inject也好,插入了一堆JS、CSS文件到HTML中,一旦部署到生產(chǎn)環(huán)境,這么多文件必然是要合并壓縮的。光是壓縮還不夠,為了解決緩存問(wèn)題,每次合并壓縮后要給最終的文件加hash,這樣每次文件內(nèi)容一變動(dòng),hash也會(huì)跟著變動(dòng),就不存在瀏覽器依然使用緩存的老文件的問(wèn)題。這樣得到最終的文件以后,肯定還要將這個(gè)文件替換回HTML中去,一大堆的script和link標(biāo)簽替換成最終合并壓縮帶hash的版本。
前面啰啰嗦嗦的一大堆工作就是這三個(gè)插件要解決的問(wèn)題了。首先,gulp-useref根據(jù)注釋將HTML中需要合并壓縮的區(qū)塊找出來(lái),對(duì)區(qū)塊內(nèi)的所有文件進(jìn)行合并。注意:它只負(fù)責(zé)合并,不負(fù)責(zé)壓縮!所以合并出來(lái)的文件我們要自行壓縮,壓縮以后調(diào)用gulp-rev負(fù)責(zé)在文件名后追加hash。最后調(diào)用gulp-rev-replace負(fù)責(zé)把最終的文件名替換回HTML中去。扯了大半天,還是直接上例子吧。先來(lái)看看HTML中的注釋?zhuān)?/p>
|
|
gulp-useref識(shí)別的就是build開(kāi)頭的注釋?zhuān)琤uild后面首先跟的是類(lèi)型擴(kuò)展名,然后后面的路徑就是build區(qū)塊中的所有文件進(jìn)行合并后的文件路徑,這個(gè)相對(duì)路徑是相對(duì)于這個(gè)HTML的路徑。上面的例子中我們用build區(qū)塊把bower和inject進(jìn)來(lái)的文件包起來(lái),這些文件就可以被gulp-useref合并了。再來(lái)看gulp中useref相關(guān)task的定義:
|
|
首先一上來(lái),先調(diào)用$.useref.assets()
函數(shù),這個(gè)函數(shù)返回一個(gè)stream,包含已經(jīng)合并后的文件??梢試L試在第9行后面加上前面介紹過(guò)的gulp-print插件.pipe($.print())
,打印出stream里的文件,發(fā)現(xiàn)就是前面HTML中4個(gè)build注釋塊后面的4個(gè)文件。注意這里調(diào)用的時(shí)候跟了一個(gè)searchPath
的參數(shù),它的用處就是指定從哪個(gè)路徑開(kāi)始尋找build區(qū)塊底下的文件。比如build區(qū)塊底下有這么一行<script src="static/js/a.js"></script>
,那最終gulp-useref將從這個(gè)路徑app/src/static/js/a.js
找到這個(gè)文件。第3到5行定義了3個(gè)filter,這主要是為了后面壓縮準(zhǔn)備的。下面正式看stream的pipe流程。先選出要處理的HTML文件,然后調(diào)用剛才得到的assets
得到合并后的4個(gè)文件,第10到12行篩選出合并后的CSS文件進(jìn)行壓縮(壓縮類(lèi)插件下篇文章再講),第13到16行篩選出app.js進(jìn)行壓縮,第17到19行篩選出lib.js進(jìn)行壓縮。之所以要區(qū)別對(duì)待app.js和lib.js,是因?yàn)閍pp.js是我們自己寫(xiě)的代碼,壓縮后要加上header(第15行,使用前面介紹過(guò)的gulp-header插件),而lib.js是第三方的各種庫(kù),直接壓縮即可。后面調(diào)用gulp-rev給壓縮后的4個(gè)文件加hash,然后調(diào)用assets.restore()
將src源換回HTML文件,這是為了后面調(diào)用$.useref()
,因?yàn)?code style="font-family:"PT Mono",Consolas,Monaco,Menlo,monospace; font-size:1em; background:rgb(238,238,238); padding:0px 0.3em; white-space:pre-wrap; word-break:break-word">$.useref()做替換的src源是HTML文件,同樣后面調(diào)用gulp-rev-replace將帶hash的文件替換回HTML,它要求的src源也必須是HTML文件。這里的順序很重要,因?yàn)檫@幾個(gè)插件接受的源不一樣,gulp-rev接受的是JS、CSS文件,而gulp-useref和gulp-rev-replace接受的是HTML。還有一個(gè)問(wèn)題:gulp-rev-replace是怎么知道gulp-rev進(jìn)行hash前后的文件名對(duì)應(yīng)關(guān)系呢?其實(shí)gulp-rev會(huì)生成一個(gè)manifest的文件,內(nèi)容是類(lèi)似下面的JSON:
|
|
當(dāng)然這個(gè)文件默認(rèn)是不會(huì)生成在文件系統(tǒng)里的,可以通過(guò).pipe($.rev.manifest())
將這個(gè)文件保存到本地。有了這個(gè)文件,gulp-rev-replace甚至可以脫離gulp-rev獨(dú)立工作哦!
好了,這篇就到這里,還有好多工具沒(méi)介紹到,留著給下篇吧。
聯(lián)系客服