gulp進階-自定義gulp插件
來自: http://hao.jser.com/archive/9224/
gulp已經成為很多項目的標配了,gulp的插件生態也十分繁榮,截至2015.1.5,npm上已經有10190款gulp插件供我們使用。我們完全可以傻瓜式地搭起一套構建。
然而,我們經常會遇到一種情況,我們好不容易按照文檔傳入對應的參數調用了插件,卻發現結果不如預期,這時候我們就要一點點去排錯,這就要求我們對gulp插件的工作原理有一定的了解。本文以實現一個gulp插件為例,講解一下gulp插件是如何工作的。
需求描述
通常,我們的構建資源為js/css/html以及其它的一些資源文件,在開發或發布階段,js/css會經過合并,壓縮,重命名等處理步驟。
有些場景下,我們不能確定經過構建后生成js/css的名稱或者數量,如此就不能在HTML文件中寫死資源的引用地址,那么該如何實現一個Gulp的插件用以將最終生成的資源文件/地址注入到HTML中呢?
假設我們需要實現的插件是這樣使用方式:
我們通過一個HTML注釋用以聲明需要依賴的資源,InlineResource 是匹配的關鍵詞,":"做為分割,/*.css$/,/*.js$/ 是聲明要依賴的文件的正則匹配。
在gulpfile.js我們需要這邊配置:
這里簡單介紹下其中的一些方法與步驟:
-
gulp.src('index.html')會讀取文件系統中當前目錄下的index.html,并生成一個可讀的Stream,用于后續的步驟消費
-
InjectResources(stream)是我們將要實現的插件,它接受一個參數用以獲取要注入到HTML中的JS/CSS,此參數應該是一個 Stream 實例,用生成一個Stream實例,用于接收并處理上一步流進來的數據
-
hash(options)是一個第三方插件,用于往當前流中的文件名添加md5串,如: gulp-hash
-
gulp.dest('dist')用于將注入資源后的HTML文件生成到當前目錄下
我們要關心的是第2點:如何接所有的資源文件并完成注入?
我們可以將該邏輯分成4個步驟
- 獲取所有的js/css資源
- 獲取所有的HTML文件
- 定位HTML中的依賴聲明
- 匹配所依賴的資源
- 生成并注入依賴的資源標簽
在開編之前,我們需要依賴一個重要的第三方庫: map-stream
map-stream 用于獲取當前流中的每一個文件數據,并且修改數據內容。
步驟1 (JS/CSS資源)
資源流會作為參數的形式傳給InjectResources方法,在此通過一個異步的實例方法獲取所有的文件對象,放到一個資源列表:
- mapStream的處理方法中獲取到的data是由gulp.src生成的 vinyl 對象,代表了一個文件
- 每一個stream都會在接受后拋出end事件
Note: mapStream的處理方法中的cb方法,第二個參數可以用于替換當前處理的文件對象
到此,我們就完成了第一步的封裝啦!
步驟2 (HTML文件)
InjectResources插件方法會返回一個 Writable Stream 實例,用于接收并處理流到InjectResources的HTML文件,mapStream的返回值就是一個writable stream。
此時,mapStream的處理方法拿到的data就是一個HTML文件對象,接下來進行內容處理。
步驟3 (定位依賴)
我們拿到的data是一個 vinyl 對象,contents屬性是文件的內容,類型可能是Buffer也可能是String, 通過toStraing()后可以獲取到字符串內容。
所有的依賴聲明都有InlineResource關鍵詞,簡單點的做法,可以通過正則來定位并替換HTML中的資源依賴:
到此,我們完成了資源依賴的定位,下一步將是獲取所依賴的資源用以替換。
步驟4 (依賴匹配)
我們將通過步驟1定義的 getResources 方法獲取所需的資源文件:
由于 getResources 是異步方法,因此需要把替換處理邏輯包裹在 getResources 的回調方法中
根據依賴聲明中的正則表達式,對資源列表一一匹配:
到此只差最后一步,將資源轉換為HTML標簽并注入到HTML中
步驟5 (資源轉換/依賴注入)
接下來的定義一個transform方法,用于將路徑列表轉換為HTML的資源標簽列表,其中引入了 path 模塊用于解析獲取文件路徑的一些信息,該模塊是node內置模塊。
最終,我們將標簽列表拼接為一個字符串來HTML中的依賴聲明(注入):
到此也就完整地實現了一個擁有基本注入功能的插件~~~~~~
One More Thing
通過上面實現的示例步驟,可以清楚了解到gulp插件的工作原理。 但要做一個易用/可定制性高的插件,我們還要繼續完善一下,例如:
- 比較資源的路徑與HTML的路徑,輸出相對路徑作為默認的標簽資源路徑
- 提供 sort 選項方法用于修改資源的注入順序
- 提供 transform 選項方法用于定制標簽中的資源路徑
-
在依賴聲明中支持 inline 聲明,用以將資源內容內聯到HTML中,例如:
-
支持命名空間,用于往同一個資源流中使用多次資源注入的區分,例如:
-
. . .
</ul> </div>