iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 4
0

所有良好的專案都需要健全的自動化系統在背後支撐,雖然實作自動化需要成本,但之後帶來的效益絕對會讓你一邊寫 Code 一邊笑到合不攏嘴。

何謂自動化?

我們提過專案的開發是有分 Development、Test 還有 Production 週期的,不同階段需要建置(Build)不同的檔案配置或環境,這些瑣碎的雜事傳統上是人工在 Terminal 上打出一系列的指令,包括編譯、轉譯、複製檔案、刪除中繼暫存檔、...等等,熟悉 Linux 的人可能會自己寫個 Shell Script 來執行這些例行性的流程,這就是自動化的精神。

再舉個例子,寫過 C 的人應該有 87% 知道 Makefile 這東西,只要把建置流程寫在 Makefile 內,再到 Terminal 輕鬆打下 make 四個字母,撲通一聲就產生可執行檔了。在這個例子中,make 的過程就是一種自動化。

為何專案需要自動化?

所以究竟為什麼要在專案中落實自動化呢?因為人為操作的過程有太多不謹慎,可能打錯指令,可能執行的流程顛倒了,還要浪費腦容量來背指令,身為一個工程師,怎麼可以容許自己做這麼蠢的事呢?另外,團隊的開發也是需要有一套共同的操作流程,不可能每一位團隊裡的成員都熟悉產品的建置流程,所以最好訂定一套一致的、語意明確的、簡易的自動化指令,例如要跑測試就統一執行 npm test,要部署程式就統一打 gulp deploy

最後幫各位歸納一下自動化帶來的優點:

  • 減少人為失誤
  • 省時、省力、省腦容量
  • 標準化團隊協作的流程

Npm

前面所述是給各位一個大方向,現在我們回到 Node 的世界。大家所熟悉的 Npm 除了可以安裝套件之外,還有另一個重要功能,就是執行腳本。

馬上給各位一個實用的例子,請在你的任意 package.json 補上以下 scripts 的部分:

{
  ...
  "scripts": {
    "clean": "rm -rf ./build"
  },
  "devDependencies": {
    ...
  },
  "dependencies": {
    ...
  }
}

接著執行 npm run clean 就會自動執行對應的 rm -rf ./build 指令了。

而 npm 其實也內建了幾個快速指令對應到 package.json 中的 scripts,例如:npm test 對應到 scripts 中的 testnpm start 對應到 scripts 中的 start,其餘依此類推,完整對應的指令可以參考 Npm 官方文件 npm-scripts

實務上我們會把 scripts 拆的越細越好,一個 script 只專注完成一件工作,如果需要自動化執行一系列的大工作,就用 && 將這些小工作串聯起來執行即可。一樣給各位一個實際用在 Boilerplate 中的例子:

{
  "scripts": {
    "test": "npm run lint && npm run mocha",
    "lint": "./node_modules/.bin/eslint --config=./.eslintrc.json ./src",
    "mocha": "mocha specs/index.js --timeout 10000",
  },
}

如此一來我只要執行 npm test 就可以自動檢查 Coding Style,接著執行 Mocha 的測試腳本了。

善用 Gulp,勿用 Grunt

利用 Npm 來實現自動化對於小專案是很足夠的,但對於 Boilerplate 則需要更進階的工具了,目前有兩套廣受好評的自動化工具:Gulp 與 Grunt。

先強調 Gulp 絕對遠勝於 Grunt。

Grunt 是 Configuration-Based,所以使用之前必須熟悉 Grunt 的設定檔結構,說難聽點就是我他媽要浪費腦容量記這雞八結構,而且可讀性之差讓我實在懶得吐槽,其次,Grunt 在處理某些複雜的操作時,必須要先建置出中繼檔案,說白話點就是它會製造垃圾,最後我最不能忍受的是,有少量需要高度客製化的流程過於複雜,已經超出 Configuration-Based 機制的極限了,所以根本就做不出來!做不出來!做不出來啊!

罵完了 Grunt 回來看看我們可愛又親切的 Gulp:

  • Program-Based
  • 可讀性高
  • 容易擴充
  • 使用 Streaming 搭配 pipe 語法,所以不會產生中繼檔案
  • 無論多複雜的流程都能以優雅的方式寫成腳本

使用 Gulp

接著我們就直接看看 Gulp 的使用方式吧:

  1. 在專案內建立一份 gulpfile.js
  2. 以 JS 撰寫各種 Gulp Tasks
  3. 執行 gulp <task-name>

Gulp Task

Gulp 中最重要的元素就是 Gulp Task,它是一個個相依或獨立的工作流程,每一個 Gulp Task 寫法都是單純且一致的。

gulp.task([dependencyTask1, dependencyTask2, ...], taskName, function() {
  // what this task is going to do
});

範例

讓我們來看個簡單的 gulpfile.js 範例(擷取自 Boilerplate):

var path = require('path');
var gulp = require('gulp');
var del = require('del');
var mergeStream = require('merge-stream');

var paths = {
  componentStyles: [
    './src/common/components/**/*.scss',
    './src/common/components/**/*.less',
    './src/common/components/**/*.styl',
    './src/common/components/**/*.css',
  ],
  statics: './src/public/**/*',
  targetDir: 'build',
};

// clean build files
gulp.task('clean', function() {
  return del.sync([
    paths.targetDir,
  ]);
});

// copy static files
gulp.task(['clean'], 'copy', function() {
  var staticTask = gulp
    .src(paths.statics)
    .pipe(gulp.dest(path.join(paths.targetDir, 'public')));
  var componentStyleTask = gulp
    .src(paths.componentStyles)
    .pipe(gulp.dest(path.join(paths.targetDir, 'common/components')));
  return mergeStream(staticTask, componentStyleTask);
});

gulp.task('default', function() {
  gulp.start('clean', 'copy');
});

當我執行 gulp clean 時,就會清除 build 資料夾;當我執行 gulp copy 時,會先確保 gulp clean 已經完成,接著才複製靜態檔案和各個 styles 檔案;當我執行 gulp default 時,會同時執行 gulp clean 及 gulp copy。另外,如果直接執行 gulp 而不指定 task name,預設就是執行 gulp default。

在本篇教學文中提到 Gulp 的目的不是要讓大家在本文中學會使用 Gulp,而是想讓讀者們知道有這樣的好工具,真正要學會它的話還是請上官網或者 Google 學習資源會比較有效率!

Boilerplate 的自動化

在我們的 Boilerplate 中大量地使用了自動化,包括 Npm 及 Gulp,來完成以下的工作:

  • 自動化 Linting
  • 自動化 Build
  • 自動化 Testing
  • 自動化 Deploy

我傾向使用 Npm 實現 CLI 指令的自動化(例如 Linting),如有牽扯到複雜的流程或邏輯檢查時,再轉用 Gulp(例如 Build、Deploy)。

自動化的過程是痛苦的

乍看之下自動化百利無一害,但其實要在專案裡面套用自動化是需要經歷陣痛期的,你需要仔細看過 CLI Tool 的說明,或是 Gulp Plugin 要如何使用、如何設定、參數怎麼下,經歷這些過程時肯定會讓你感到挫折、感到煩燥。實做專案的自動化可能是個痛點,但我還是強烈建議各位讀者要扎扎實實地面對它,因為手動的成本真的太高昂了,長痛不如短痛!

筆者心情小補帖

還記得我剛接觸自動化時踩了一個大雷...

那時觀察 Open Source 界的風向發現大家都在用 Grunt,所以我也跟風先學 Grunt,不是我在亂蓋,但我真的是花了整整一個星期努力研究這糞物,瘋狂 Google 各種教學文卻一無所成,完全寫不出我想要的自動化流程。最後放棄 Grunt,隨手花了 30 分鐘看個 Gulp 官方介紹,X你X的我就這樣學會 Gulp 了,隨後花了 2 ~ 3 個小時就建出理想的流程...。至今每當我想起 Grunt 都還是覺得噩夢一場。


上一篇
Day 02 - File Organization
下一篇
Day 05 - Backend - Technique Stack
系列文
30 天打造 MERN Stack Boilerplate30

尚未有邦友留言

立即登入留言