用程式碼整合流程:gulp


Posted by saffran on 2021-02-25

gulp 簡介

gulp 就是一個 task manager

我可以把各種 task 寫在一個 gulp file 裡面,透過 gulp 的很多 plugin 去幫我執行這些任務,讓我可以很方便地用「程式化的方式」去管理這些 tasks

有一個類似的服務叫做 IFTTT,就是 If This Then That 的簡寫

初探 gulp

npm init 新開一個 project 的目的只是為了要有 package.json 這個檔案

如果發現指令用 gulp 會出現錯誤,但改為使用 npx gulp 就正常,這是因為 global 的東西有裝錯,所以用 npx 就會用專案裡面的 gulp 來跑(不會去找 global 的)

輸入 npm install --save-dev gulp 安裝完之後,如果用 gulp --version 出現錯誤,那可以改用 npx gulp --version 來查看版本,如果有出現版本,就表示安裝成功了

在 gulpfile.js 裡面

把官網上的程式碼複製貼上:

function defaultTask(cb) {
  // place code for your default task here
  cb();
}

exports.default = defaultTask

上面這段的意思是:
gulp 可以分成很多個 task,每個 task 就是一個 function
exports.default = defaultTask 意思就是「gulp 會幫我執行 .default 這個 function」,default 是 task 的名稱

如果在 CLI 輸入指令 npx gulp,預設就會執行 default 這個 task

所以如果把 task 名稱改為 aaa,像是這樣:

function defaultTask(cb) {
  // place code for your default task here
  cb();
}

exports.aaa = defaultTask

在 CLI 就要輸入指令 npx gulp aaa

gulp 實戰

要如何用 gulp 串接 sass 和 babel 的任務呢?

在 gulpfile.js 裡面:

function defaultTask(cb) {
  // place code for your default task here
  cb();
}

exports.default = defaultTask

cb 是 gulp 提供的一個 callback function,在 defaultTask 函式裡面呼叫 cb() 就是在跟 gulp 說「所有的任務都完成了」

gulp 有提供一些內建的 function,例如 srcdest (都是一個 function)
const { src, dest } = require('gulp')srcdest 拿進來

如果是用 gulp 內建的東西,可以想成是一個「資料流」的概念,就像是在寫 JS 陣列時,可以這樣一直串接 function:

return [a, b, c].map().filter().reduce()

在用 gulp 時也是這種資料流的概念:

const { src, dest } = require('gulp');

function defaultTask() {
  // place code for your default task here
  return src('src/*.js')
    .pipe(dest('dist'))
}

exports.default = defaultTask

可以在 src() 小括號裡面放入「我要做事情的程式碼」

src('src/*.js') 意思就是:會去找 src 資料夾裡面所有的 .js 檔案

連接要用一個 .pipe()(這是 gulp 的規則),.pipe() 小括號裡面再放入要做的事情

dest('dist') 意思就是:我要把它寫入到 dist 這個資料夾

寫完 task 之後,就可以輸入指令 npx gulp 來執行這個 task,執行完後,就可以看到多了一個 dist 資料夾,裡面有 demo.js 檔案

gulp 做的事情就是:把 src 資料夾裡面的 demo.js 檔案,複製到 dist 資料夾

babel 搭配 gulp

詳細可參考 gulp-babel

是採用 plugin 的形式:
需要安裝 gulp 的 plugin,再搭配 babel 自己的東西
因為 @babel/core@babel/preset-env 都已經裝好了,所以就只需要安裝 gulp-babel 這個 plugin,輸入指令 npm install --save-dev gulp-babel

裝好 plugin 之後,就可以開始使用了:
文件的範例程式碼:

const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('default', () =>
    gulp.src('src/app.js')
        .pipe(babel({
            presets: ['@babel/env']
        }))
        .pipe(gulp.dest('dist'))
);

我在 gulpfile.js 寫的程式碼:
const babel = require('gulp-babel') 就是把 gulp-babel 這個 plugin 引入進來
在中間用 .pipe(babel()) 就可以讓 babel 幫我編譯

const { src, dest } = require('gulp');
const babel = require('gulp-babel');

function defaultTask() {
  // place code for your default task here
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

exports.default = defaultTask

這裡我在 babel() 小括號裡面不用再寫一次 babel 的 preset 設定 {presets: ['@babel/env']} 是因為我在專案資料夾裡面已經有一個 .babelrc 的檔案,檔案裡面就已經設定好 preset 了:

{
  "presets": [
    "@babel/preset-env"
  ]
}

寫完之後,就可以輸入指令 npx gulp 來執行這個 task
執行完後,在 dist 資料夾裡面的 demo.js 就會是經過 babel 編譯過後的程式碼了!

接著,可以把「babel 編譯」的這個 task 用一個 compileJS 的 function 包起來:

const { src, dest } = require('gulp');
const babel = require('gulp-babel');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

function defaultTask() {
  // place code for your default task here

}

exports.default = defaultTask

接著,就可來寫 sass 的任務

sass 搭配 gulp

詳細可參考 gulp-sass

首先,先安裝需要的 plugin,輸入指令 npm install node-sass gulp-sass --save-dev

裝完 plugin 之後就可以開始使用了
在 gulpfile.js 裡面這樣寫:
var sass = require('gulp-sass') 就是:把 gulp-sass 這個 plugin 引入進來

sass.compiler = require('node-sass') 就是:指定 sass 的 compiler

const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const sass = require('gulp-sass');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

function defaultTask() {
  // place code for your default task here
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = defaultTask

寫完之後,就可以輸入指令 npx gulp 來執行 task
執行完後,就會多一個 css 資料夾,裡面就會是把 style.sass 編譯過後的 style.css 檔案了

接著,也把這個 sass 的任務包成一個 compileCSS 的 function:

const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const sass = require('gulp-sass');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = defaultTask

series 串接任務(按照順序,依序執行)

用 gulp 內建的 series 來執行 compileJScompileCSS 這兩個 tasks

  • const { src, dest, series } = require('gulp') 要記得把 series 先引入進來

  • exports.default = series(compileCSS, compileJS) 會按照順序:先執行 compileCSS,再執行 compileJS

因為是先後分開執行,所花費的時間會較多

const { src, dest, series } = require('gulp');
const babel = require('gulp-babel');
const sass = require('gulp-sass');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = series(compileCSS, compileJS)

parallel 串接任務(會同時執行)

exports.default = parallel(compileCSS, compileJS)parallel 會同時執行 compileCSScompileJS
因為是同時執行,所花費的時間會較少

const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const sass = require('gulp-sass');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = parallel(compileCSS, compileJS)

gulp-uglify (壓縮 JS 程式碼)

詳細可參考 Conditional plugins

首先,先輸入指令 npm install --save-dev gulp-uglify 來安裝 plugin
接著,引入 plugin 並加上 .pipe(uglify())

const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(uglify())
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = parallel(compileCSS, compileJS)

然後用 npx gulp 執行任務後,就可以看到 JS 檔案已經被壓縮了(壓縮成一行,去掉所有換行、空白),通常在 server 上看到的都會是這樣 uglify 之後的檔案(為了節省檔案空間)

gulp-clean-css (壓縮 CSS 檔案)

詳細可參考 gulp-clean-css

首先,先輸入指令 npm install gulp-clean-css --save-dev 來安裝 plugin

.pipe(cleanCSS({ compatibility: 'ie8' })) 來壓縮 CSS 檔案

  • compatibility: 'ie8' 可以設定「要支援到哪些瀏覽器」,會依據設定來決定要在 CSS 加上哪些瀏覽器的 prefix
const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');
const rename = require('gulp-rename');
const cleanCSS = require('gulp-clean-css');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
    .pipe(cleanCSS({ compatibility: 'ie8' }))
    .pipe(rename({ extname: '.min.css' }))
    .pipe(dest('css'))
}

exports.default = parallel(compileCSS, compileJS)

gulp-rename (更改檔案名稱)

首先,先輸入指令 npm install --save-dev gulp-rename 來安裝 plugin

通常會把有做過 uglify 或 minify 的 JS 和 CSS 檔案,副檔名改叫做 .min.js 和 .min.css,這樣才能和原始檔案做區別

.pipe(rename({ extname: 'min.js' })) 更改檔案名稱

const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');
const rename = require('gulp-rename');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = parallel(compileCSS, compileJS)

也可以這樣寫,在 compileJS() 先輸出「沒有壓縮過的」,再輸出「有壓縮過的」:

const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');
const rename = require('gulp-rename');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
}

exports.default = parallel(compileCSS, compileJS)

執行個別任務

如果只想要執行 compileCSS 這個任務,就把 compileCSS exports 出去,然後在 CLI 輸入指令 npx gulp compileCSS 就可以只跑 compileCSS 這個任務

const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const sass = require('gulp-sass');
const rename = require('gulp-rename');
const cleanCSS = require('gulp-clean-css');

sass.compiler = require('node-sass');

function compileJS() {
  return src('src/*.js')
    .pipe(babel())
    .pipe(dest('dist'))
    .pipe(uglify())
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest('dist'))
}

function compileCSS() {
  return src('src/*.sass')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('css'))
    .pipe(cleanCSS({ compatibility: 'ie8' }))
    .pipe(rename({ extname: '.min.css' }))
    .pipe(dest('css'))
}

exports.compileCSS = compileCSS
exports.default = parallel(compileCSS, compileJS)

執行預設的任務

exports.default = parallel(compileCSS, compileJS) 意思就是:當我在 CLI 只輸入 npx gulp (沒有加上任務名稱),那就會執行 default 的任務,也就是 parallel(compileCSS, compileJS) (平行、同時跑這兩個任務)

gulp 在實務上的應用

可參考 Lidemy 官網的 gulpfile.js

這裡有先定義好每個不同的 task(都分別做不同的事情) 並 exports 出去:

exports.clean = clean;
exports.default = build;
exports.js = js;
exports.css = css
exports.img = img
exports.beforeEnd = beforeEnd
exports.sprite = gulp.series(sprite, css);

gulp.watch() 監測檔案變動

watch 這個 function 就是會去監測裡面有寫的每個檔案,當檔案有變動的時候就重新執行該任務

function watch(done) {
  if (env !== 'production') {
    gulp.watch(`${base.src}/scss/**/*`, css);
    gulp.watch(`${base.src}/html/**/*`, html);
    gulp.watch(`${base.src}/js/**/*`, js);
    gulp.watch(`${base.src}/image/*`, img);
    gulp.watch(`${base.src}/image/*`, gulp.series(sprite, css));
  }
  done();
}

img() 壓縮圖片

可以把 img 讀進來 > 把 img 壓縮之後 > 再把 img 存到我指定的地方

這樣就不需要自己去做圖片的壓縮

function img() {
  return src(paths.image.src)
    .pipe(imageMin())
    .pipe(dest(paths.image.dest))
    .pipe(connect.reload())
}

#gulp







Related Posts

API 留言板練習-Part 3 : 增加[載入更多]功能

API 留言板練習-Part 3 : 增加[載入更多]功能

漫談傳輸介面-SATA

漫談傳輸介面-SATA

DAY 06 : 樹

DAY 06 : 樹


Comments