ใช้ GulpJs กับเว็บไซต์ของเรา
Tags: gulp, javascript, ไทย, scss
Published: 2019-06-22 15:26:31

---
title: ใช้ GulpJs กับเว็บไซต์ของเรา
published: true
description: บทความนี้จะพูดถืงการติดตั้งและใช้งาน Gulp เพื่อจัดการแปลง SCSS ไฟล์ให้เป็น CSS ไฟล์ รวมทั้งจะทำการแปลงไฟล์รูปให้เป็นไฟล์ webp พร้อมทั้งการลดขนาด JS และ CSS ตบท้ายด้วยการทำ Cache Busting
tags: gulp ,javascript ,ไทย ,scss
cover_image: https://thepracticaldev.s3.amazonaws.com/i/9gov2fsnpduvmiqmngm6.jpg
---

ผมจะสร้างเว็บไซต์ใหม่ด้วย [Hugo](https://gohugo.io/)
โดยที่จะใช้ [GulpJs](https://gulpjs.com/) ช่วยในการจัดการ css js
และ รูปภาพ ที่เขียนบทความนี้เพราะตอนนี้ผมกำลังมีโปรเจ็คจะสร้างเว็บ Blog
ใหม่เอาไว้สำหรับเขียนงานแปล เพราะช่วงนี้หานิทาน และนิยายเก่าๆอย่าง อีสป และนิทานกริมม์
มาเล่าให้ลูกฟังก่อนนอน เลยอยากจะเอามาแปะไว้ให้คนอื่นๆที่สนใจได้อ่านกันด้วย

> ถ้าหากใครต้องการเรียนรู้การสร้างเว็บไซต์ด้วย Hugo ผมแนะนำให้เข้าไปดูบทความของผมก่อนหน้านี้ได้ที่ [Hugo + Firebase](https://atthaphon.urairat.me/2017/12/05/hugo---firebase/)

##  สิ่งที่ต้องมีต้องใช้ในบทความนี้

ผมใช้ Mac ในการทำงาน ดังนั้นเราจะใช้ Command line เป็นหลักในการจัดการ
ติดตั้งเครื่องมือต่างๆรวมถึงการสร้างตัวโปรเจ็ค โดยสิ่งที่เราจำเป็นต้องมีในเครื่องคือ [Homebrew](https://brew.sh/) [NodeJs](https://nodejs.org/en/)
และ [npm](https://www.npmjs.com/)
หากว่าคุณยังไม่มีก็ให้ลิงค์ไปที่เว็บไซต์แต่และลิงค์และทำการติดตั้งได้เลย


## สร้าง Gulp pipeline

บทความนี้จะพูดถืงการติดตั้งและใช้งาน Gulp เพื่อจัดการ แปลง SCSS ไฟล์ให้เป็น CSS ไฟล์
สำหรับใช้กับเว็บไซต์ ในส่วนของรูปภาพนั้นเราจะทำการแปลงไฟล์รูปอย่าง .jpg หรือ .png ให้เป็นไฟล์ .jpg
และ ลดขนาด JS ไฟล์กับ CSS ด้วยการ compressed โดยในท้ายที่สุด เราจะเพิ่มเลขเวอร์ชั่นเมื่อทำการคอมไพล์
สำหรับป้องกัน cache (สิ่งนี้เรียกกว่า Cache Busting)

เราจะใช้ Gulp กับ Hugo ดังนั้น เราจะต้องทำการวางไฟล์ทั้งหลายของเราที่ถูกคอมไพล์เรียบร้อยแล้วเอาไว้ที่ `static/` โฟลเดอร์
และเก็บไฟล์ที่เราเอาไว้ใช้ทำงานหรือไฟล์ที่ยังไม่ได้รับการ คอมไพล์ไว้ในโฟลเดอร์ `src/`

เริ่มต้นโดยการสร้างโฟลเดอร์สำหรับเก็บไฟล์ต่างที่เราจะเอาไว้ทำงานในโปรเจ็คนี้
ซึ่งประกอบไปด้วยโฟลเดอร์ `scss`, `js` และ `images`

```bash
$ mkdir -p src/{scss,js,images}
```

### ติดตั้ง Gulp

เราจะทำการติดตั้งโดยใช้ `--yes` flag เพื่อความรวดเร็ว

```bash
$ npm init --yes
```
> `--yes` flag จะช่วยสร้างค่าพื้นฐานสำหรับ npm โปรเจ็คของเราให้โดยอัตโนมัติ

### แพ็กเกจ ที่จำเป็นต้องใช้

ต่อไปนี้คือ npm package ที่เราจะใช้ในบทความนี้

- [gulp](https://www.npmjs.com/package/gulp)
- [gulp-sass](https://www.npmjs.com/package/gulp-sass)
- [gulp-autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer)
- [gulp-uglify](https://www.npmjs.com/package/gulp-uglify)
- [gulp-hash](https://www.npmjs.com/package/gulp-hash)
- [gulp-concat](https://www.npmjs.com/package/gulp-concat)
- [gulp-webp](https://www.npmjs.com/package/gulp-webp)
- [del](https://www.npmjs.com/package/del)

ต่อไปเราก็ทำการติดตั้งแพ็กเกจทั้งหมด

```base
$ npm install --save-dev gulp gulp-sass gulp-autoprefixer gulp-uglify gulp-hash gulp-concat gulp-webp del
```

### สร้าง gulpfile.js

เราต้องสร้าง gulp ไฟล์เพื่อเอาไว้จัดการกับกับเพ็กเกจที่เราได้ทำการติดตั้งไว้

```base
$ touch gulpfile.js
```

เมื่อเราสร้าง gulpfile.js เรียบร้อย ต่อไปนี้เราก็จะต้องทำการเรียกใช้งานเพ็กเกจ และสร้าง task
นั่นคือการมอบหมายงานต่างๆให้กับ gulp เพื่อจัดการกับส่ิงต่างๆที่เราต้องการ

### require เพ็กเกจ

```javascript
var	gulp				= require("gulp"),
	sass				= require("gulp-sass"),
	autoprefixer		= require("gulp-autoprefixer"),
	uglify				= require("gulp-uglify"),
	hash				= require("gulp-hash"),
	concat				= require("gulp-concat"),
	webp				= require("gulp-webp"),
	del					= require("del");

```
### สร้าง Task สำหรับ คอมไฟล์ scss

ในส่วนของการสร้าง Task สำหรับแปลง scss เป็น css เราจะเริ่มต้นด้วยการลบไฟล์ทั้งหมดภายใต้โฟลเดอร์ `static/css`

ต่อจากนั้นเราก็จะทำการเรียกฟังก์ชั่น `autoprefixer` จากแพ็กเกจ `gulp-autoprefixer`
เพื่อทำการเพิ่ม vendor prefix ให้เราโดยอัตโนมัติ ซึ่งตัวฟังก์ชั่นจะทำการเพิ่มโค้ด vendor prefix ที่เข้ากันได้กับ browser
แต่ละตัวโดยนับถอยหลังไป 20 version

จากนั้นเราก็ทำการคอมไพล์ scss โดยการเพิ่มออฟชั่น `outputStyle : "comporessed"` เพื่อทำการ minify ให้กับ css ของเรา

ขั้นตอนต่อไปคือการเพิ่ม hash ให้กับชื่อไฟล์ css ที่เราจะทำการคอมไพล์
เริ่มจากการเรียกฟังก์ชั่น hash เพื่อทำการเพิ่มรหัสให้กับไฟล์ css ที่เราจะทำการคอมไฟล์จาก scss ของเรา
สำหรับทำ Cache Busting และสร้างคอมไพล์ไฟล์ตัวใหม่ภายใต้โฟลเดอร์ `static/css`

และในขั้นตอนสุดท้าย เราก็จะทำการเพิ่ม key, value mapping สำหรับการเรียกใช้ css ไฟล์
ลงไปใน `rev_manifest.json` ที่อยู่ในโฟลเดอร์ `data`


```javascript
gulp.task("scss", function () {
	del(["static/css/**/*"])
	gulp.src("src/scss/**/*.scss")
		.pipe(autoprefixer({browsers : ["last 20 versions"]}))
		.pipe(sass({outputStyle : "compressed"}))
		.pipe(hash())
		.pipe(gulp.dest("static/css"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})
```

### สร้าง Task สำหรับคอมไพล์ Javascript

ในส่วนของการสร้าง Task สำหรับ Javascript ไฟล์ เราจะเริ่มต้นด้วยการลบไฟล์ทั้งหมดภายใต้โฟลเดอร์ `static/js`

ต่อไปเราก็จะสั่งให้ Gulp จัดการกับไฟล์ต่างๆภายใต้โฟลเดอร์ `src/js` โดยจะทำการรวมไฟล์ javascript
ที่มีทั้งหมดให้เป็นไฟล์เดียวด้วย `concat` ฟังก์ชั่นที่มีอยู่ใน `gulp-concat` เพ็กเกจที่เราได้ทำการติดตั้งเอาไว้

จากนั้นก็ทำการ minify javascript ด้วย `uglify` ฟังก์ชั่นจากเพ็จเกจ `gulp-uglify`

ขั้นตอนต่อไปคือการเพิ่ม hash ให้กับชื่อไฟล์เช่นเดียวกันกับ css ที่เราได้ทำการพูดถึงไปแล้วข้างต้น
เริ่มจากการเรียกฟังก์ชั่น hash เพื่อทำการเพิ่มรหัสให้กับไฟล์ `scripts.js` สำหรับทำ Cache Busting
และสร้างคอมไพล์ไฟล์ตัวใหม่ภายใต้โฟลเดอร์ `static/js`

และในขั้นตอนสุดท้าย เราก็จะทำการเพิ่ม key, value mapping สำหรับการเรียกใช้ scripts ไฟล์
ลงไปใน `rev_manifest.json` ที่อยู่ในโฟลเดอร์ `data`

```javascript
gulp.task("js", function () {
	del(["static/js/**/*"])
	gulp.src("src/js/**/*.js")
		.pipe(concat('scripts.js'))
		.pipe(uglify())
		.pipe(hash())
		.pipe(gulp.dest("static/js"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})
```

### สร้าง Task สำหรับคอมไพล์ Images

ในส่วนของการสร้าง Task สำหรับ images ของเรานั้นจะเริ่มต้นด้วยการลบไฟล์ทั้งหมดภายใต้โฟลเดอร์ `static/images`

ต่อไปเราก็จะสั่งให้ Gulp จัดการกับไฟล์ต่างๆภายใต้โฟลเดอร์ `src/images`
โดยจะทำการแปลงไฟล์รูปภาพที่อยู่ภายใต้โฟลเดอร์นี้ให้เป็นไฟล์ webp เพื่อเอาใว้ใช้สำหรับเว็บไซต์โดยตั้งค่า quality ให้อยู่ที่ 50

ขั้นตอนต่อไปคือการเพิ่ม hash ให้กับชื่อไฟล์เช่นเดียวกันกับ css และ js ที่เราได้ทำการพูดถึงไปแล้วข้างต้น
เริ่มจากการเรียกฟังก์ชั่น hash เพื่อทำการเพิ่มรหัสให้กับไฟล์รูปภาพแต่ละรูปที่ได้ทำการแปลงเป็นไฟล์ webp เป็นที่เรียบร้อยแล้ว
สำหรับทำ Cache Busting และสร้างไฟล์ตัวใหม่ภายใต้โฟลเดอร์ `static/images`

และในขั้นตอนสุดท้าย เราก็จะทำการเพิ่ม key, value mapping สำหรับการเรียกใช้ scripts ไฟล์
ลงไปใน `rev_manifest.json` ที่อยู่ในโฟลเดอร์ `data`
```javascript
gulp.task("images", function () {
	del(["static/images/**/*"])
	gulp.src("src/images/**/*")
		.pipe(webp({quality: 50}))
		.pipe(hash())
		.pipe(gulp.dest("static/images"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})
```

### สั่งให้ Gulp Update ไฟล์เมื่อมีการแก้ไขไฟล์ (gulp watch)

เราจะทำการสั่งให้ Gulp ทำการตรวจสอบ ไฟล์ทุกไฟล์ภายใต้โฟลเดอร์ `src/scss`, `src/js` และ `src/images`
เมื่อมีการเปลี่ยนแปลงของไฟล์ภายใต้โฟลเดอร์เหล่านี้ Gulp ก็จะทำประมวลผลใหม่ตาม Tasks ที่เราได้สร้างไว้ข้างต้น

```javascript
gulp.task("watch", ["scss", "js", "images"], function () {
	gulp.watch("src/scss/**/*", ["scss"])
	gulp.watch("src/js/**/*", ["js"])
	gulp.watch("src/images/**/*", ["images"])
})
```

## เกี่ยวกับ Cache Busting

บางครั้งเมื่อเราทำการอัพเดตเว็บไซต์ของเราเรียบร้อยแล้ว แต่บราวเซอร์ของผู้ใช้งานมีการเก็บ cache
ทำให้ไฟล์ที่เราได้ทำการแก้ไขไปเช่นไฟล์ css js และ
รูปภาพไม่ถูกโหลดมาใหม่ทำให้เว็บของเราไม่สามารถแสดงผลในส่วนที่เราเพิ่งทำการอัพเดตใหม่ได้

การทำ Cache Busting เป็นทางออกของปัญหานี้ สำหรับบทความนี้เราจะทำ Cache Busting ด้วยการนำ hash
ที่ได้รับมาจากฟังก์ชั่นที่เราเรียกใช้จาก `gulp-hash` แพ็กเกจ
มาต่อท้ายชื่อไฟล์ของเราหลังจากทำการคอมไฟล์ เพื่อทำให้บราวเซอร์เห็นว่ามีการอัพเดต
และโหลดข้อมูลมาใหม่ ดังนั้นจะทำให้การอัพเดตของเราไม่ถูกละเลยจากบราวเซอร์ของผู้ใช้งาน

โดยทั่วไปผมจะทำการเพิ่มชื่อของไฟล์ css, js และ รูปภาพ เช่น `<link rel="stylesheet" href="http://example.com/css/styles.css" media="all" />`
เข้าไปในตัวโค้ดโดยตรงเลย แต่ในกรณีที่เราใช้เทคนิค Cache Busting นั้น
ชื่อไฟล์ของเราจะมีการเปลี่ยนแปลงเมื่อมีการคอมไพล์เกิดขึ้น

ต่อไปเรามาลองดูการเรียกไฟล์ที่มีการเปลี่ยนแปลงชื่อของตัวไฟล์ด้วยเทคนิคนี้กัน

### การเรียกใช้งาน rev_manifest.json

จากตัวอย่าง เราได้ทำการเก็บ `rev_manifest.json` ไว้ในโฟลเดอร์ `data`
ซี่งหน้าตาของข้อมูลในไฟล์ควรที่จะมีหน้าตาประมาณนี้

```json
{"scripts.js":"scripts-efc857a6.js","styles.css":"styles-74c912f3.css","image-1.jpg":"image-1-9f3k238s.jpg","image-2.jpg":"image-2-86s42ds3.jpg"}
```
อย่างที่ทราบกันดีว่าเราใช้ Hugo ในการสร้างเว็บไซต์ดังนั้นเราจะใช้ Golang
ฟังก์ชั่นเพื่อช่วยในการหา index ของชื่อไฟล์จาก `rev_manifest.json`

จากตัวอย่างซึ่งโดยปรกติแล้วเราจะทำการเรียนกไฟล์ css แบบนี้

```html
<link rel="stylesheet" href="http://example.com/css/styles.css" media="all" />
<script src="http://example.com/js/scripts.js" async=""></script>
```

เราจะใช้ Hugo Variable ที่มีมาให้เพื่อชี้ไปยังไฟล์ rev_manifest.json และหา index
เพื่อนำค่าที่ได้มาใช้ แบบนี้

```html
<link rel="stylesheet" href="http://example.com/css/{{ index .Site.Data.rev_manifest "styles.css" }}" media="all" />
<script src="http://example.com/js/{{index .Site.Data.rev_manifest "scriptes.js" }}" async=""></script>
```
ในส่วนของ Images เราก็ใช้วิธีเดียวกัน โดยการหา index ชื่อของรูปที่เราต้องการจะใช้งาน
เราอาจทำการสร้าง [shortcode](https://gohugo.io/content-management/shortcodes/)
เพื่อความง่ายในการใช้งานเมื่อเราต้องการเพิ่มรูปให้กับบทความที่เรากำลังเขียนอยู่

## Final Code

เมื่อเรานำเอาโค้ดทั้งหมดมารวมกันก็จะได้เป็น

gulpfile.js
```javascript

var	gulp				= require("gulp"),
	sass				= require("gulp-sass"),
	autoprefixer		= require("gulp-autoprefixer"),
	uglify				= require("gulp-uglify"),
	hash				= require("gulp-hash"),
	concat				= require("gulp-concat"),
	webp				= require("gulp-webp"),
	del					= require("del");

gulp.task("scss", function () {
	del(["static/css/**/*"])
	gulp.src("src/scss/**/*.scss")
		.pipe(autoprefixer({browsers : ["last 20 versions"]}))
		.pipe(sass({outputStyle : "compressed"}))
		.pipe(hash())
		.pipe(gulp.dest("static/css"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})

gulp.task("js", function () {
	del(["static/js/**/*"])
	gulp.src("src/js/**/*.js")
		.pipe(concat('scripts.js'))
		.pipe(uglify())
		.pipe(hash())
		.pipe(gulp.dest("static/js"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})

gulp.task("images", function () {
	del(["static/images/**/*"])
	gulp.src("src/images/**/*")
		.pipe(webp({quality: 50}))
		.pipe(hash())
		.pipe(gulp.dest("static/images"))
		.pipe(hash.manifest("rev_manifest.json"))
		.pipe(gulp.dest("data"))
})

gulp.task("watch", ["scss", "js", "images"], function () {
	gulp.watch("src/scss/**/*", ["scss"])
	gulp.watch("src/js/**/*", ["js"])
	gulp.watch("src/images/**/*", ["images"])
})
```
เราได้เรียนรู้วิธีการใช้ Gulp กับโปรเจ็คของเราเป็นที่เรียบร้อยแล้ว
ซึ่งจะช่วยให้ขนาดของโปรเจ็คของเราไม่บวมมากเวลาอัพโหลดไฟล์ที่ต้องใช้เข้าไปยัง Server
นี่เป็นการช่วยประหยัดเงินประหยัดทองในการเช่า Server
อีกทั้งไฟล์และรูปภาพที่มีขนาดเล็กลงก็ยังส่งผลให้ความเร็วในการโหลดหน้าเว็บของเราเพิ่มขึ้นอีกด้วย
สุดท้ายและท้ายที่สุดเราก็ยังสามารถป้องกันปัญหาเกี่ยวกับ Cache ไปได้อีกด้วย