認識 Gulp.js — 拆分 Gulp 任務

拆分 Gulp 任務到不同檔案,管理更方便!

Zong-Rong Huang
9 min readJan 20, 2023
https://github.com/gulpjs/artwork

概述

當專案架構變複雜,gulpfile.js 裡的公開及私有任務數量也更加龐大,在維護和執行指令時更加不便。

可以將 gulpfile.js 依照功能、專案或其他分類拆分成多個檔案,達到關注點分離 (separation of concerns) 的目的。日後維護和執行會更加輕鬆。

本文會介紹如何拆分 gulpfile.js 檔案及如何執行任務:

  • 依功能拆分為多個 gulpfile.js 檔案
  • 依專案拆分 gulpfile.js 檔案

依功能拆分為多個 gulpfile.js 檔案

當 Gulp 任務數量眾多,可以將任務拆分成多個模組檔案進行管理,例如將 JavaScript、CSS 和 HTML 的處理任務放在不同模組檔案中。

如此一來,不僅方便新增、修改和搜尋任務。要使用任務時,再將模組檔案的任務集中匯入到相同的 index.js 檔案即可。

接下來,會為範例專案中的 JavaScript、CSS 和 HTML 檔案各自建立壓縮內容的任務。

範例架構如下:

|-- 專案/
|-- index.html
|-- css/
|-- index.css
|-- mobile.css
|-- js/
|-- index.js
  1. 安裝要使用的套件及外掛程式:
npm install --save-dev gulp gulp-cssnano gulp-htmlmin gulp-uglify

2. 在專案根目錄處建立名為 gulpfile.js 資料夾,在裡面新增 index.js 檔案及其他存放任務的模組檔案。新的專案結構如下:

|-- 專案/
|-- index.html
|-- css/
|-- index.css
|-- mobile.css
|-- js/
|-- index.js
|-- gulpfile.js/
|-- index.js
|-- cssTasks.js
|-- jsTasks.js
|-- htmlTasks.js

對於 Gulp 來說,gulpfile.js 資料夾內的 index.js 檔案等於平常使用的 gulpfile.js 檔案,都是 Gulp 存取任務函式的唯一進入點。

3. 在個別模組檔案內建立壓縮任務。

// cssTasks.js
const {src, dest} = require('gulp')
const cssnano = require('gulp-cssnano')

exports.minifyCSS = function(){
return src('css/*.css')
.pipe(cssnano())
.pipe(dest('dist/css'))
}
// jsTasks.js
const {src, dest} = require('gulp')
const uglify = require('gulp-uglify')

exports.uglifyJS = function(){
return src('js/*.js')
.pipe(uglify())
.pipe(dest('dist/js'))
}
// htmlTasks.js
const {src, dest} = require('gulp')
const htmlmin = require('gulp-htmlmin')

exports.minifyHTML = function(){
return src("*.html")
.pipe(htmlmin({ collapseWhitespace: true }))
.pipe(dest("dist/"));
}

要注意的是,在模組檔案內指定輸入和輸出的檔案路徑時,必須以 gulpfile.js 資料夾為基準點,而不是以模組檔案為基準點。

換句話說,要將 gulpfile.js 資料夾當成是一個單純的檔案,如同未拆分時的 gulpfile.js 檔案。所有的路徑皆從這個資料夾為基準點。

4. 將模組檔案的任務集中匯入至 index.js。在 index.js 中可以直接使用匯入的任務,或是重新進行組裝建立新的任務。

// index.js

const { src, dest, parallel, watch } = require("gulp");
const { minifyCSS } = require("./cssTasks");
const { minifyHTML } = require("./htmlTasks");
const { uglifyJS } = require("./jsTasks");

exports.default = function () {
watch(
["*.html", "js/*.js", "css/*.css"],
{ ignoreInitial: false },
parallel(minifyCSS, minifyHTML, uglifyJS)
);
};

此處利用匯入的任務建立新的監聽任務,再匯出任務以供執行。

5. 在終端機輸入 Gulp 指令就能執行任務:

gulp

依專案拆分 gulpfile.js 檔案

另一種可能的情況是大專案資料夾內有多個小專案資料夾。小專案各自有自己的 Gulp 任務可以使用。所有小專案的Gulp 任務皆集中在大專案根目錄的 gulpfile.js 檔案內。

|-- 大專案/
|-- package.json
|-- gulpfile.js
|-- 小專案1/
|-- index.html
|-- styles.css
|-- index.js
|-- ...
|-- 小專案2/
|-- 小專案3/

要為某個小專案新增、修改任務時,很可能要連帶調整其他小專案的 Gulp 任務,處理起來可能不方便。當小專案數量變多時,更加不輕鬆且容易出錯。

這時候可以將大專案的 gulpfile.js 拆分至各個小專案內。讓小專案有獨立的 gulpfile.js 檔案。

執行小專案時,只需要用到小專案的 gulpfile.js 檔案,不會和其他小專案共用任務。

|-- 大專案/
|-- package.json
|-- 小專案1/
|-- gulpfile.js
|-- index.html
|-- styles.css
|-- index.js
|-- ...
|-- 小專案2/
|-- gulpfile.js
|-- ...
|-- 小專案3/
|-- gulpfile.js
|-- ...

在小專案 gulpfile.js 檔案內撰寫任務的方式,完全與為單一專案撰寫 Gulp 任務的方式相同。

差別在於執行 Gulp 任務的方式。如果要執行某個小專案的 Gulp 任務,有兩種方式:

  • 在小專案根目錄執行 Gulp 指令
  • 在大專案根目錄執行 Gulp 指令

在小專案根目錄執行 Gulp 任務頗符合直覺。然而,如果同時有多個小專案要執行 Gulp 指令,就得反覆切換資料夾,不是那麼方便。

在大專案根目錄執行 Gulp 指令會是更方便的方式!

Gulp CLI 內建 --gulpfile [檔案路徑] 指令,能夠選擇特定路徑的 gulpfile.js 執行:

查看小專案 1 的 Gulp 任務清單:

gulp --tasks --gulpfile ./小專案1/gulpfile.js

執行小專案 1 的預設任務:

# OK
gulp --gulpfile ./小專案1/gulpfile.js

# OK
gulp default --gulpfile ./小專案1/gulpfile.js

執行小專案 1 的 minifyCSS 任務:

gulp minifyCSS --gulpfile ./小專案1/gulpfile.js

此時 Gulp 指令已經變長不少。建議在大專案根目錄的 package.json 中建立對應的 npm 指令,不僅可以集中管理,執行時更方便。

例如為小專案 1 及所有專案建立 build 任務:

// package.json

"scripts": {
"build_p1": "gulp build --gulpfile ./小專案1/gulpfile.js",
"build_all": "gulp build --gulpfile ./小專案1/gulpfile.js & gulp build --gulpfile ./小專案2/gulpfile.js & gulp build --gulpfile ./小專案3/gulpfile.js"
}

只需要在大專案根目錄執行 npm run build_p1npm run build_all 就好,非常精簡!

結論

當 gulpfile.js 檔案的任務數量變多,維護難度增加時,可以依據不同功能或專案將任務拆分至不同檔案。

有兩種建議作法:

  • 在 gulpfile.js 資料夾中,將任務拆分為不同的模組檔案,再將模組檔案匯入到相同的 index.js 檔案。
  • 將任務拆分至多個 gulpfile.js 檔案,執行任務時指令加上 --gulpfile 和對應的檔案路徑。

這樣一來管理 Gulp 任務時會更加方便,也減少出錯的可能。

--

--

Zong-Rong Huang

Frontend web developer/technical writer that writes to learn and self-entertain. I’m based in Taiwan.