快速建立一個 Gulp4 專案(Browsersync + Sass) gulpfile.js 筆記

Ivy Ho
IvyCodeFive
Published in
10 min readMay 19, 2021

最近第一次練習用 gulp 4 寫法撰寫 gulpfile.js,參考了這篇文章和範例

參考文章:
Quick setup for Gulp 4 + Browsersync + Sass
(文章內有影片,不熟悉初始化專案環境的朋友們可以參考👍🏻)

參考範例檔案下載 :
GitHub repo

於是想寫成筆記文章以加深印象✏️

以下筆記文章, 撰寫的 gulpfile.js 會跟參考文章所提供的範例有一點點不一樣

  • 多加了另一篇文章提供的方法 ,先將 path name 整合成一個物件
  • 多載入了套件 gulp-load-plugins、 gulp-sourcemaps

但大致相同~

最終檔案結構說明

  • app 資料夾 : 會放編譯前的 js 及 scss 檔案
  • dist 資料夾 : 用來放 "編譯後" 的 js 及 css 檔案
    (由 gulp task 生成,我們不會去直接編輯它們)

初始化專案環境

* 確定 node 已安裝

$node -v
$npm -v

1. 載入全域的 gulp cli

$npm install gulp-cli -g

2. 初始化 npm 專案,產生 package.json

  • -y 表示對所有預設內容回答 yes,等於 npm init +狂按 enter
$npm init -y

3. 載入 gulp 及其他需要的套件

$npm install gulp postcss gulp-postcss cssnano gulp-sass gulp-terser browser-sync gulp-load-plugins -D
  • -D 等於 --save-dev,表示只在開發環境使用這些套件,載入完成後會在 package.json 中 "devDependencies" 欄位出現 :
"devDependencies": {
"browser-sync": "^2.26.14",
"cssnano": "^5.0.2",
"gulp": "^4.0.2",
"gulp-load-plugins": "^2.0.6",
"gulp-postcss": "^9.0.0",
"gulp-sass": "^4.1.0",
"gulp-terser": "^2.0.1",
"postcss": "^8.2.15",
"gulp-sourcemaps": "^3.0.0",
}

套件說明

  1. gulp-sass : 可以將 .scss 檔案編譯成 .css 檔案
  2. gulp-postcss : postcss 是一個允許使用 JS 套件轉換樣式的工具。可再依需求載入其他套件,這裡我們載入 cssnano 就是它的套件之一。在 gulp 中我們使用 gulp-postcss,一樣需載入 postcss。
  3. gulp-terser : 用來壓縮 JS 檔案。
  4. gulp-load-plugins : 可以簡化我們 gulpfile.js 上面的程式碼。文章下面會示範,只要套件是"gulp-" 開頭,就可以利用這個套件來載入,不需在上面 require。
  5. browser-sync : 能在本地端開啟一個 server ,使我們可以在開發時同步看到網頁畫面。
  6. gulp-sourcemaps : 方便我們在瀏覽器 DevTools 中查找樣式時,可以回朔到特定的 scss 檔案。

撰寫 gulpfile.js

  • 在根目錄新增一個 gulpfile.js 檔案

1. 匯入特定 gulp api

const { src, dest, watch, series, parallel} = require('gulp');
  • 匯入我們會使用的特定 gulp api,如此一來,當我們需要用到時,就可以直接呼叫,例如直接使用 src(),不須使用 gulp.src() 來呼叫。

2. 匯入套件

const $ = require('gulp-load-plugins')() 
const cssnano = require('cssnano');
const browsersync = require('browser-sync').create();
  • 將我們剛剛載入的 npm 套件匯入到 gulpfile.js 當中,作為模組,以便運用在我們的 gulp task function 內。
  • 因為有載入 gulp-load-plugins 套件,等等在 task function 中我們如果要使用 'gulp-' 開頭的套件,就可以在前面加 '$.' 來匯入使用,不需在檔案上方 require。
  • 不是 'gulp-' 開頭的套件仍需要 require 匯入
  • 需特別注意 browser-sync 匯入後要加上.create()來初始化(使用方法可參考文件)。可用來建立多個 server name,但我們這邊只需要一個。

3. 整合 path name

我們可以先將檔案路徑整理成一個物件,存在 files 變數,方便等等在 gulp task 內引用,就不需要一直重複打。

const files = {
scssPath: 'app/scss/*.scss',
jsPath: 'app/js/*.js'
}

4. 建立 gulp 任務

gulp 4 與 gulp 3 的其中一個差異在於,gulp4 將 task 改用一般的函式寫法。

scss task

function scssTask(){
return src(files.scssPath)
.pipe($.sourcemaps.init())
.pipe($.sass())
.pipe($.postcss([cssnano()]))
.pipe($.sourcemaps.write('.'))
.pipe(dest('dist')
);
}
  1. src() 為 gulp api,用來指定檔案路徑
  2. sourcemaps
  • 此範例是使用剛剛載入的 gulp-sourcemaps 套件寫法
  • gulp4 內建有自己的 sourcemaps 功能,可以參考此文件寫法。

3. 使用 .pipe() 來連接任務中的子任務

4. gulp-sass 會將 .scss檔案 編譯成 .css檔案

5. cssnano 用來壓縮 css 檔案

6. 存到 dist 資料夾內

JS task

  • 用 gulp-terser 套件來壓縮 JS 檔案
function jsTask(){
return src(files.jsPath)
.pipe($.sourcemaps.init())
.pipe($.terser())
.pipe($.sourcemaps.write('.'))
.pipe(dest('dist')
);
}

Browsersync 2 個任務

  1. browsersyncServe task:
function browsersyncServe(cb){
browsersync.init({
server: {
baseDir: '.'
}
});
cb();
}
  • 初始化 local server
  • cb 參數為一個 callback function,在 Gulp 中,所有任務都是非同步的 JS function,所以如果 function 不需要 return 東西,我們需要使用 callback function 來明確的表示它已經執行完畢。其實就是 done 回調函數的用法。
  • 因為我們的 index.html 放在根目錄,所以設定 server:{ baseDir: '.')

2. BrowsersyncReload task :

  • code 有改變時重新啟動 Browsersync,會用在等等的 watch task 裡面。
function browsersyncReload(cb){
browsersync.reload();
cb();
}

Watch task :

function watchTask(){
watch('*.html', browsersyncReload);
watch([files.scssPath, files.jsPath], series(scssTask, jsTask,
browsersyncReload))
}
  • 監測到程式碼有變更,就重新執行需要的任務。
  1. 監測 .html 檔案,如有變更就重新執行 browsersyncReload task
    (因為我們不希望在變更 html 檔案後執行 scss task 和 js task,所以分開寫成兩個 watch)
  2. 監測 .scss、.js 檔案,如有變更就執行 scss task, js task, 以及 browsersyncReload task

Default task

exports.default = series(
scssTask,
jsTask,
browsersyncServe,
watchTask
)
  • 最後,將所有任務名稱放進 default 內,當我們下指令 gulp ,便可以一次執行所有任務。
  • 使用 series(),所以會按指定順序一次運行一個任務
    (如果使用 parallel() 則會以任何順序同時運行任務。依照需求,兩者也可以混用)

完整 gulpfile.js

const { src, dest, watch, series, parallel} = require('gulp');
const $ = require('gulp-load-plugins')()
const cssnano = require('cssnano');
const browsersync = require('browser-sync').create();
const files = {
scssPath: 'app/scss/*.scss',
jsPath: 'app/js/*.js'
}
function scssTask(){
return src(files.scssPath)
.pipe($.sourcemaps.init())
.pipe($.sass())
.pipe($.postcss([cssnano()]))
.pipe($.sourcemaps.write('.'))
.pipe(dest('dist')
);
}
function jsTask(){
return src(files.jsPath)
.pipe($.sourcemaps.init())
.pipe($.terser())
.pipe($.sourcemaps.write('.'))
.pipe(dest('dist')
);
}
function browsersyncServe(cb){
browsersync.init({
server: {
baseDir: '.'
}
})
cb();
}
function browsersyncReload(cb){
browsersync.reload();
cb();
}
function watchTask(){
watch('*.html', browsersyncReload);
watch([files.scssPath, files.jsPath], series(scssTask, jsTask, browsersyncReload))
}
exports.default = series(
scssTask,
jsTask,
browsersyncServe,
watchTask
)

--

--

Ivy Ho
IvyCodeFive

"You don't have to be great to start, but you have to start to be great."