[Tool Note] — 關於Gulp
Introduction & 前言
如果你為了前端切版每次都要從 HTML 開始建立檔案,然後 CSS 再來 JavaScript…等等,而且碰到多頁面的版面手足無措,那你就可以考慮看看這篇文章了。
開始前先提醒一下文章較長,您可以重頭開始看怎麼建立 Gulp 抑或是滑到下方選擇觀看 安裝後的Gulp筆記 或 **關鍵懶人包目錄**!!
之前玩過 Webpack 時就一直聽過 Gulp,但是一直沒有去研究過,一直以為兩個是差不多性質的東西,應該一個就夠用了,但是藉由這次的六角切版直播班真的接觸到了 Gulp,我想是時候好好研究研究一番了。
溫馨提醒:如果還沒看過 Webpack 或是您已經是 Webpack 高手,建議可以先參考看看我的前兩篇文章,如果您只是想研究 Gulp 其實只看本篇文章也沒關係,[Tool Notes] — 關於Webpack #1 - 第一次就上手 及 [Tool Notes] — 關於Webpack #2 - Babel? 如果有歡念錯誤歡迎各路高手不吝社指教。
地雷警告:由於 Gulp 自己也還不是很熟,本篇文章就是用來筆記加練習的,如果有觀念錯誤也歡迎各路高手不吝社指教。
關鍵懶人包目錄
這邊提供給已經安裝過並且懂的大概的 npm 操作流程,只想看 Gulp 怎麼操作的人:
Gulp 是什麼?
和 Webpack 一樣,剛學前端在切版時我們很長就是創建一個新的 HTML CSS JavaScript…等等,碰到多頁面的版面我們就是 index_1.html index_2.html index_3.html,久了就會覺得麻煩,身為工程師,不就是要把麻煩的事情簡單化嗎?!
所以在後來就出現了基於 Node.js(剛接觸前端不久後可能就會常常碰到)、NPM 的**自動化建構工具(註1)**,幫你省去很多重複且枯燥的步驟。
註1:就字面上的意思大概已經解釋了 87% 簡單說就是下個指令之後幫你執行一些不必重複做的工作,比方說 編譯、壓縮、命名或是開發時用一種寫法,上正式環境換一種寫法。
使用說明?
其實仔細看 Gulp 跟 Webpack 的官網介紹,會發現兩個非常像,但你實際使用,會發現 Gulp 更注重於 任務 的概念,而 Webpack 注重於 打包 及 效能 方面。
但其實兩種工具官方都有很詳盡的介紹,只要跟著走,基本都沒什麼問題!甚至最後你還能將兩者合併使用呢!
但筆記不是寫心情日記,今天還是要認真的跑過一次流程,下面開始就會介紹怎麼把 Gulp 跑過一遍。
釣竿來
這邊建議還是要有一點 JavaScript 的基礎,如果沒有沒關係,如果有會比較輕鬆。
首先您必須要先安裝 Node.js 之後才能使用 npm,這邊先不用搞懂他們在幹嘛,只要記得之後常常會碰到 node_module 而這裡面放滿你會用到的前端套件。
安裝方法很簡單,可以到 Node.js 的官方網站下載安裝檔安裝,或是參考我的這篇文章安裝 [Tool Notes] — 關於Webpack #1 - 第一次就上手 裡面有提到 Node.js 怎麼安裝。
安裝完成記得打開終端機輸入
node --version
如果安裝成功你會看到v版本號
,就是安裝成功了,記得也要輸入npm --version
就能看到有沒有把 npm 安裝成功!
趕快發車
前置作業了之後我們要安裝 全域的Gulp-cli 及 專案裡的Gulp,前者的安裝讓使用者可以在任何地方透過 cli 指令(比如 gulp) 來下指令,後者讓使用者可以使用 gulp API 來自定義建構的任務。
首先安裝全域的 gulp-cli
1 | $ npm install gulp-cli -g // 安裝全域的 gulp-cli |
接著進到你要實作的專案底下輸入以下指令(安裝路徑記得要對喔!!),縮寫的 i 為 install 縮寫 -D 為 –save-dev 的縮寫,意指只安裝到 package.json 的 devDependencies,不理解的話先略過沒關係
1 | $ npm i -D gulp // 安裝 gulp 到專案 |
安裝完成輸入 gulp --version
,如果看見下圖 Local version: Unknown 就代表你目前在錯誤的目錄,一定要進到你要實作的專案底下安裝才會有版本號出現!
初始化專案
以往我們都是從 index.html 開始建立,這邊我們換個做法,到你要實作的專案資料夾下,先在終端機輸入 npm init
或 npm init -y
,第一次建立建議都輸入前者,因為接下來會跳出一連串的問題給你作答,大致就是問你 專案名稱 專案介紹 作者 或是 是否要連上Git…等等,建議跑過一次流程之後再試試後面的輸入方法,後者可以省略那堆問題!
因為我們是練習,所以就是一路 Enter 下來,最後會在你的資料夾內看到 package.json 檔案,這邊我們先來介紹一下這個檔案是幹嘛的。
以往我們會把需要用的套件直接用 CDN 方式插入在 index.html 裡面,就像下圖所示。
但是隨著引用的套件越來越多,會越來越雜亂,而且也不好管理版本,所以後來的 package.json 就是在做這件事情,裡面詳細記載你用了什麼套件,還有這個專案的詳細介紹(沒錯 就是你前面略過的那一大串),至於版本號等等還會有個檔案叫 package-lock.json 記載。
前置步驟的最後在輸入下列語法,就會安裝記載在 package.json 裡的所有套件,這也是為什麼我們最後只需要上傳 原始檔 及 package.json 給下一個人,他就會知道你用了什麼套件並且怎麼修改,而我們也不需要把一大包套件上傳到空間上去。
1 | $ npm install |
除此之外使用 package.json 管理套件還有個好處,通常使用 cdn 方式引入套件,如果對方存放套件的 Server 掛掉了,或是連結甚至網路掛掉了,基本上就無法使用這個套件了,但安裝在 node_module 等於是安裝在本地,即使網路斷了,或是來源遺失了,至少你還保留一份套件檔案在這邊,前提是你必須在掛掉之前安裝Q。
安裝完成後就會出現 package-lock.json,這就會記載你安裝套件的版本號啦!
預告太長了
先別急,這邊正式要開始了。這邊開始 Gulp 做法跟 Webpack 有點像,後者會在資料夾建立 webpack.config.js,前者會在資料夾建立 gulpfile.js,這就是我們的 核心設定 檔案了。
按照官網的指示,我們先在專案資料夾創建 gulpfile.js 然後在裡面貼上下列程式碼:
1 | function defaultTask(cb) { |
之後在終端機上輸入 gulp(記得要到專案目錄底下喔!),會出現下面的結果,先講解一下,終端機輸入 gulp 後面如果不帶任何字串,預設就是 gulp default,而我們要有個概念 gulp 都是以任務為出發點再跑的,意思就是跑一個 default 的任務,而前面我們已經寫好了,預設的 default 任務就是 function defaultTask()
,內容就是 console.log(123);
。
沒錯!到這邊你就完成了,就是這麼簡單。
不是幼幼班
當然事情沒有這麼容易,我們想做到的事情和現在還差十萬八千里遠,但放心,如果是 Webpack 的話目前應該還有二十萬八千里遠(以一個菜鳥過來人的上手經驗來說,有誤別砲我)。
在我們有了 任務 的概念之後,我們還要先有一個基礎知識,前面提到我們有說我們會安裝很多套件在 node_module 立面並且記載在 package.json,那是不是 npm install 之後出現 package-lock.json 就可以使用了呢?
這邊就要講解一下了,通常透過 Node.js 安裝的套件,在那個專案都會有一個 **進入點(在前面 npm init 的時候會問你要不要指定檔案 不要就是預設 index.js)**,而我們想使用套件就必須在 進入點 把套件 import 或是 required 近來,意思其實跟 cdn 在 index.html 插入一行 <link rel="stylesheet" href="./plugins/bootstrap-v4.3.1/css/bootstrap.min.css" />
是差不多的,接著你就能在 進入點 的那隻檔案使用那個套件,最後只要你的檔案可以吃得到 進入點 就能使用套件。
Gulp 4.0 和以前的寫法可能有些許差異,因為菜雞我沒用過以前版本,但 4.0 的寫法在 4.0 以前會無法使用,例如陣列寫法改為 series() 及 parallel()
怎麼引入套件
這邊我們會引領您記得幾個 Gulp 要記得的 API,以後您會常常使用到它!
首先這邊就以 Gulp 為例子,當我們有很多任務要執行的時候,我們該怎麼做呢?
在檔案內改為以下的例子:
1 | // 引入套件 及 套件API |
第一句就是我們引進套件及套件的API(方法),而 4.0 跟以往最大的差異就在多了 series
及 parallel
,前者為任務一個結束才會換下一個,後者為同步進行,我不管前面完成沒有,反正包在 parallel
裡的我全部都一次進行。
而範例的進行結果就是後面會等 taskOne 運行結束(記得如果沒有 Return 在 Function 立面都要設定一個 end 宣吿,例如 cb()),結束後包在 parallel
裡面的不管順序都會一起進行。
順帶一提,最前方的
const { series, parallel } = require('gulp');
亦可寫為const gulp = require('gulp');
然後在需要使用的地方加上gulp.series
或gulp.parallel
,例如最後一句可改為exports.default = gulp.series(taskOne, gulp.parallel(taskTwo, taskThree))
,意思為使用 gulp 的 APIseries
及parallel
。
除了前面寫的 cb() 當作結束任務外,任務還可以使用諸如
promise
、event emitter
、child process
或observable
…等等,詳細可參考官方指南,如果都不用就是必須向前面寫的,要有一個 callback(例:cb())。
關鍵懶人包一
上述例子做完你已經學會了基本的兩個API,series
及 parallel
,只要記得要呼叫任務就是使用這兩個,前者為不同步呼叫,後者為同步呼叫!
還有什麼全都拿出來!
再學會前面兩個基本的 API 後,這邊開始我們要學另外兩個基本的 API;想要 自動化建構 我們最基本需要的事情就是 操作檔案。
為什麼 Gulp 的 API 可以操作檔案呢?因為他接受 glob 參數,詳細的介紹可參考此處。
首先我們要記得第一個 API 為 **src()**,意思就是從哪個路徑讀取檔案,使用方式和前方一樣,先引入 const gulp = require('gulp');
,然後呼叫 gulp.src('./檔案路徑')
,或是在最前方改為 const { src } = require('gulp');
,然後使用 src('./檔案路徑')
。
按照官網的說法,取得檔案後會生成一個 **Node流(stream)**,意思是蝦咪呢?Steam 我還理解(誤)
翻了網上的解釋,它是一種數據處理的方法,從輸入到輸出,可以稱為一個流,他並不是像傳統的讀寫文件,一次一個,而是隨讀取的數據多寡,處理其內容,而在大數據之中就非常強大,例如檔案可能超過你的空間容量,不可能將整個文件塞進你的空間在讀取,而這時後 Node流(stream) 就發揮作用了。
抱歉我實在是找不到圖片,如果比喻有誤還請手下留情。
而 Node流(stream) 分為四種流:
清流?流派?
流大致可分為四種類型:
Writable - 可寫入的流
Readable - 可讀取數據的流
Duplex - 可讀又可寫的流
Transform - 在讀取過程中可以修改或轉換數據的 Duplex 流
上面貼這麼多流得介紹網站如果你英文不好可以參考這邊 中文流。
繼續開車
接著繼續學習我們的 Gulp API,簡單理解下,從我們發起讀取檔案 gulp.src('./檔案路徑')
後,這時候就會產生 Node流(stream) 而這時候我們就可以在這之間去對 Node流(stream) 做動作。
然而怎麼做動作可以依靠 Node流(stream) 提供的方法 .pipe()
,適用於 轉換流(Transform streams) 或是 **寫流(Writable streams)**,透過 .pipe()
我們可以把想做的事情透過方法寫在 .pipe()
內,比如 .pipe(babel())
。
這邊就會學到我們接下來第二個基本的 API,**dest()**。
如果不想理解上方這麼多細節的話,請先在專案底下創建 app 資料夾,裡面在創建一個 assets 資料夾,最後再放入 js 資料夾,然後創建兩個 .js 在裡面,這邊範例一個名稱為 main.js 另一個為 second.js,接著在 核心檔案 gulpfile.js 內輸入以下程式碼:
1 | // 來源, 目的地 |
接著在終端機輸入 **gulp(或 gulp default)**,就會開始編譯,這時候你會發現多出一個 dist 資料夾,仔細瞧,你的自動化已經踏出一大步了!
關鍵懶人包二
上述例子做完你又學會了基本的兩個API,src()
及 dest()
,只要記得想要操作檔案,就要取檔案及輸出檔案,前者為取得檔案,後者為輸出檔案,而這中間的過程可以對檔案做一些壞壞的事情想做的事情!
補充說明
上面提到的 .pipe(dest())
為 gulp
提供的一個 API,而我們有一些多的套件都可以加入到這裡面,比方說 ES6
編譯,接著來學習怎麼使用套件吧!
先輸入 npm i -D gulp-babel @babel/core @babel/preset-env
然後在 gulpfile.js 上方引入 const babel = require('gulp-babel');
編譯套件,接著在把前面提到的範例程式碼做一下改編:
1 | // 來源, 目的地 |
然後到我們的 ./app/assets/js/main.js
把裡面的程式碼改為下列範例,範例為 ES6 的箭頭函示:
1 | const exampleWord = () => "Hello Gulp!" |
之後一樣在專案底下開啟終端機輸入 gulp(或 gulp default)
,結果再到 ./dist
資料夾查看編譯出來的 .js 檔案,你會發現語法已經被編譯成普通的 JavaScript 了!
像是 babel() 這個 API 就是套件提供給我們的,我們只需要塞入 .pipe()
然後在看套件有什麼需要設定的;而像 模板、SCSS、複製檔案…等等,差不多都是這樣的做法,突然覺得還挺簡單得吧(當我沒說哈哈?!
你畢業了
學會四個基本的 API,其實你已經學得差不多了,接著就是剩下的套件學習,是不是覺得跟 Webpack 基礎理解差不多,只要抓到基礎得點,剩下再延伸進入研究就差不多了,雖然我還是覺得 Gulp 比較好上手。
接下來這邊會提供一份範例,有興趣的同學可以抓下來研究,也可以自己重無到有去創建,記住只要抓住那四個基礎,如何開始任務,如何解決同步非同步任務,如何取得及輸出檔案,你就能慢慢上手。
恭喜幼幼班畢業
超級比一比
雖然這樣比好像不是正確的,但目前自己練習完 Webpack 及 Gulp 之後的整理心得如下圖,如果想入門一探這兩種自動建構工具的學習基礎:
搞懂這幾個基礎點之後剩下的就是一些設定及套件研究了!
你要的魚在這
這邊提供一個範例,包含 JS 編譯、Sass/Scss 編譯、檔案複製、檔案監聽、模擬瀏覽器…等等,之後會再補上 HTML 模板部分,可至 GitHtb 的 README.md 查看安裝的過程。
Gulp 範例傳送門 => 點我
Conclusion & 結論
其實以前已經想研究 Gulp 很久了,這次會接觸也是因為六角切版直播班,但因為是切版為主,課程沒有太在 Gulp 著墨,但我想工程師的本質都是想追根究底的,碰到東西還是會想研究學習一番,畢竟之後開始處理問題總不能一直拿老師的範例吧。
這次為 Gulp 初體驗,我想還有許多改進空間,像是之後還會再考慮如何把 Webpack 融入進 Gulp,而我想後者的學習門檻似乎比前者還要低一點,恰巧最近碰到需要把前端規劃出一個有效且快速的部署環境,我想 Gulp 應該會很適合我。
這邊有機會在寫下一篇 Gulp 了,感謝各路大神的觀看,如果有錯還請各路大神不吝社指教,在此小弟先謝過了!
我們都是在錯誤和學習中成長,但憑著興趣或快樂學習的速度將會是倍數成長!
參考網站
- Gulp 官網 — Gulp EN
- Gulp 中文官網 — Gulp CH
- 鐵人邦 ayugioh2003 大大 — [試著把切版專案升級到 gulp4.0 吧] Day03 Gulp4 環境配置
- Roya’s Blog — Gulp 前端自動化 - 使用 Babel 編譯 ES6
- Ray Blog — 這是在講 Gulp 不是飲料是任務自動化工具這件事
- 六角 — 切版直播班
- Rex — [Tool Notes] — 關於Webpack #1 - 第一次就上手
- Rex — [Tool Notes] — 關於Webpack #2 - Babel?
後記
這篇文其實實在 2020/05/05 晚上寫的,但 hexo 似乎無法用兩個一樣名字的 .md 所以只好把日期往後延一天了。