ตอนนี้เราจะมาใช้ ES6 syntax สำหรับการเขียนภาษา JavaScript กัน ซึ่งนับว่าเป็นอะไรที่พัฒนาขึ้นมาดีขึ้นมากเทียบกับ ES5 syntax "แบบเดิมๆ" ซึ่ง browsers ปัจจุบัน รวมถึงเหล่า JS environments ทั้งหลายที่มักใช้ๆ กันต่างเข้าใจ ES5 เป็นอย่างดี แต่ไม่เข้าใจ ES6 ดังนั้นทำให้เราต้องใช้เครื่องมือที่เรียกว่า Babel ในการแปลงจาก ES6 ไปเป็น ES5
เพื่อที่จะใช้งาน Babel เราจะมีการใช้ Gulp ด้วย Gulp นั้นเป็น task runner ซึ่งจะคล้ายคลึงกับ tasks ที่อยู่ใน scripts
ในไฟล์ package.json
แต่ว่าเราจะเขียน task เหล่านั้นด้วยโค้ดภาษา JavaScript ซึ่งแน่นอนว่ามันก็ง่ายกว่า และชัดเจนกว่าเขียนเป็น JSON เยอะ ดังนั้น เราจะติดตั้ง Gulp และ Babel plugin สำหรับ Gulp ด้วย โดยทำตามนี้
- สั่ง
yarn add --dev gulp
- สั่ง
yarn add --dev gulp-babel
- สั่ง
yarn add --dev babel-preset-latest
- สั่ง
yarn add --dev del
(ใช้สำหรับclean
task เดี๋ยวจะมีในตัวอย่างด้านล่าง) - ในไฟล์
package.json
เพิ่ม fieldbabel
ไว้เป็นค่า config ให้กับ Babel เพื่อให้ Babel นั้นใช้ presets ตัวล่าสุด แบบนี้
"babel": {
"presets": [
"latest"
]
},
หมายเหตุ: ไฟล์ .babelrc
ที่อยู่ที่โปรเจคของเราก็สามารถใช้งานแทน field babel
ที่อยู่ในไฟล์ package.json
ได้เหมือนกัน ซึ่งในความเป็นจริง โปรเจคของเราก็จะมีขนาดใหญ่ขึ้นมากเรื่องๆ ดังนั้นการเอา config ของ Babel ไว้ใน package.json
ก็เป็นสิ่งที่ดี เว้นแต่ว่า Babel config จะเริ่มใหญ่ขึ้นเรื่อยๆ จึงค่อยแยกออกมาอีกไฟล์
- ย้ายไฟล์
index.js
ไปไว้ในโฟลเดอร์ชื่อว่าsrc
(สร้างโฟลเดอร์ใหม่ หากยังไม่มี) ในโฟลเดอร์นี้เราจะเขียนโค้ดภาษา JavaScript แบบ ES6 กัน โดยเมื่อทำการ compile โค้ด ES6 เสร็จแล้ว โค้ด ES5 ที่ได้จะอยู่ในโฟลเดอร์lib
โดยที่ Gulp กับ Babel จะเป็นคนจัดการในการ compile และสร้างโฟลเดอร์lib
ขึ้นมาเอง แล้วให้ทำการลบโค้ดที่เกี่ยวกับcolor
ที่หลงเหลือจากตอนที่แล้ว และเขียนโค้ดด้านล่างแทน
const str = 'ES6';
console.log(`Hello ${str}`);
โค้ดนี้เรามีการใช้สิ่งที่เรียกว่า template string ซึ่งเป็นฟีเจอร์ใหม่ใน ES6 ที่เราสามารถแทรกตัวแปรเข้าไปใน string ได้โดยตรง ไม่จำเป็นต้องทำการต่อ string แบบเดิมๆ อีกต่อไป โดยใช้ ${}
สังเกตดูจะเห็นว่า template strings จะถูกเขียนโดยใช้สัญลักษณ์ backquotes (` )
- ต่อมาให้เราทำการสร้างไฟล์
gulpfile.js
ในไฟล์ให้เขียนโค้ดดังต่อไปนี้
const gulp = require('gulp');
const babel = require('gulp-babel');
const del = require('del');
const exec = require('child_process').exec;
const paths = {
allSrcJs: 'src/**/*.js',
libDir: 'lib',
};
gulp.task('clean', () => {
return del(paths.libDir);
});
gulp.task('build', ['clean'], () => {
return gulp.src(paths.allSrcJs)
.pipe(babel())
.pipe(gulp.dest(paths.libDir));
});
gulp.task('main', ['build'], (callback) => {
exec(`node ${paths.libDir}`, (error, stdout) => {
console.log(stdout);
return callback(error);
});
});
gulp.task('watch', () => {
gulp.watch(paths.allSrcJs, ['main']);
});
gulp.task('default', ['watch', 'main']);
เรามาอธิบายโค้ดทั้งหมดนี้ให้เข้าใจกันก่อน
Gulp นั้นมี API ที่ค่อนข้างจะเข้าใจง่าย ใช้คำตรงไปตรงมา สิ่งที่เราทำก็คือ เรานิยาม gulp.task
จำนวนมากที่อ้างอิงถึงไฟล์ได้ผ่าน gulp.src
แล้ว chain คำสั่งต่อๆ กันด้วยคำสั่ง .pipe()
(เช่น คำสั่ง babel()
เป็นต้น) และได้ output ออกมาเป็นไฟล์ใหม่ โดยใช้ gulp.dest
ในการอ้างอิง path ปลายทาง
นอกจากนี้เรายังมี gulp.watch
ที่จะมองการเปลี่ยนแปลงของไฟล์ที่เราสนใจ เมื่อไฟล์เกิดการเปลี่ยนแปลง เราจะสั่งงานตาม tasks ที่เราระบุไว้ใน array (เช่น ['build']
) เป็นพารามิเตอร์ตัวที่สอง นอกจากนี้ยังมี API อีกมากมายให้ใช้ ซึ่งหากสนใจสามารถดูใน documentation ของ Gulp ได้
ในบรรทัดต้นๆ เราประกาศ object paths
เพื่อเก็บ path ของไฟล์ที่เราจะใช้ทั้งหมด นั่นคือ path ของ source file allSrcJs
และ path ของปลายทางที่เราต้องการ libDir
และการทำแบบนี้ก็ช่วยให้เรา Don't Repeat Yourself (DRY) ได้อีกด้วย
หลังจากนั้น เรานิยาม tasks (งาน) มา 5 งาน: build
, clean
, main
, watch
, และ default
build
จะเป็น task ที่ Babel จะถูกเรียกใช้เพื่อแปลง source file ทั้งหมดที่เราเขียนในโฟลเดอร์src
และบันทึกผลสุดท้ายที่แปลงสำเร็จแล้วลงโฟลเดอร์lib
clean
เป็น task ที่เราจะทำการเคลียร์ข้อมูลในโฟลเดอร์lib
ที่ถูกสร้างขึ้นมาเองด้วย Gulp ทั้งหมด โดยจะถูกเรียกก่อนที่จะทำ taskbuild
ทุกครั้ง ซึ่ง task นี้มีประโยชน์ในการลบไฟล์เก่าทิ้งไปให้หมด เพื่อให้เหมือนเป็นการที่เราจะ sync ผลลัพธ์ให้ตรงกับต้นฉบับในโฟลเดอร์src
อยู่ตลอด โดยเราใช้ package ชื่อว่าdel
ในการลบไฟล์ทิ้ง ซึ่งเหมาะกับการใช้คู่กันกับ Gulp's stream (ซึ่งวิธีนี้เป็นคำแนะนำ ในการลบไฟล์ที่ดี โดยใช้ Gulp)main
มีค่าใกล้เคียงกับการสั่งnode .
ในครั้งที่แล้ว แต่ครั้งนี้เราต้องการรันโค้ดที่อยู่ภายในlib/index.js
แทน เพราะปกติ Node จะมองหาindex.js
เป็นค่า default เราแค่สั่งnode lib
ก็ได้ผลเหมือนเดิม (ในที่นี้เราใช้ตัวแปรlibDir
เลยเพื่อให้เรานั้นไม่ทำอะไรซ้ำซ้อนหลายๆ รอบ) ส่วนคำสั่งrequire('child_process').exec
และexec
ใน task นั้นเป็น function ของ Node ที่ให้เรา execute shell command ได้ เราส่งตัวแปรstdout
ไปให้กับconsole.log()
และให้ return error ที่อาจเกิดขึ้นได้โดยใช้ callback function ของgulp.task
มาช่วย โอเค หากคุณยังรู้สึกงงๆ อยู่บ้าง ก็ไม่เป็นไร ขอสรุปว่า task นี้จะแค่สั่งnode lib
ให้กับเราโดยอัตโนมัติwatch
จะสั่งรัน taskmain
เมื่อ filesystem พบว่ามีการเปลี่ยนแปลงเกิดขึ้นกับ file ที่เราระบุไว้ในฟังก์ชัน (ในทีนี้ก็คือ หากไฟล์ในโฟลเดอร์src
เกิดการเปลี่ยนแปลง ให้สั่งงาน taskmain
ทันที)default
เป็น task พิเศษที่จะทำงานเมื่อเราสั่งแค่gulp
เฉยๆ จาก Command Line (CLI) ในเคสนี้เราจะให้รัน taskwatch
กับmain
หมายเหตุ: คุณอาจจะกำลังสงสัยว่าทำไมเราเขียน ES6 ใน Gulp file ของเราได้ ทั้งๆ ที่เราไม่ได้แปลงมันเป็น ES5 ด้วย Babel เลยนี่หนา? นั่นเป็นเพราะว่าเราเลือกใช้ Node เวอร์ชันใหม่ที่รองรับการใช้ ES6 อยู่แต่แรกแล้ว ทำให้เราเขียนโค้ด ES6 บน Gulp file ได้ (ดังนั้น โปรดเช็คให้มั่นใจว่าใช้งาน Node เวอร์ชันที่ใหม่กว่า 6.5.0 เป็นต้นไปแล้ว โดยใช้คำสั่ง node -v
ในการเช็ค)
เอาล่ะ! เรามาดูกันว่า task เหล่านี้ใช้งานได้
- ใน
package.json
เปลี่ยนstart
script ให้เป็น"start": "gulp"
- สั่ง
yarn start
เราควรจะเห็น "Hello ES6" ขึ้นมา แล้ว Gulp จะเริ่มมองการเปลี่ยนแปลงของไฟล์ ให้ลองเขียนโค้ดผิดๆ ดูในไฟล์src/index.js
เพื่อดูว่า Gulp จะแสดงผล error โดยอัตโนมัติ หลังจากที่คุณเซฟไฟล์ใหม่แล้ว - เพิ่มโฟลเดอร์
/lib/
ไปใน.gitignore
(ไม่จำเป็นที่จะต้อง commit โค้ดในโฟลเดอรนี้ลงไป เพราะอย่างไรก็ตามเราสามารถทำการ compile ได้อยู่แล้ว เพียงแค่มี source code)