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,例如 src
和 dest
(都是一個 function)
用 const { src, dest } = require('gulp')
把 src
和 dest
拿進來
如果是用 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
來執行 compileJS
和 compileCSS
這兩個 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
會同時執行 compileCSS
和 compileJS
因為是同時執行,所花費的時間會較少
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 在實務上的應用
這裡有先定義好每個不同的 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())
}