CommonJS Modules (using “Require”)
CommonJS คือรูปแบบการเขียน JavaScript แบบแบ่ง Code ออกเป็นสัดเป็นส่วนหรือเป็น Unit ย่อยๆ เพื่อให้ง่ายต่อการพัฒนาโปรเจคขนาดใหญ่ขึ้น จุดแข็งคือการทำงานแบบ Synchronous
ตัวอย่าง
// somefile.js
exports.log = function (text) {
console.log(text)
}// anotherfile.js
const logFn = require('./somefile.js')// using
logFn('Hello World')
ES6 Modules (using “Import”)
ในสมัยก่อนนั้น JavaScript ยังไม่สามารถจัดการเรื่องของ Modules ได้ด้วยตัวเอง ทำให้ต้องใช้ Libs ต่างๆเข้ามาช่วยจัดการ เช่น CommonJS/AMD เป็นต้น ซึ่งการมาของ ES2015/ES6 นั้นได้มีการอัพเดทฟังก์ชั่น ES6 Modules เข้ามาทำให้ตัว JavaScript สามารถจัดการการเขียนแบบ Modules ได้ด้วยตัวเองแล้ว
ตัวอย่าง
// somefile.js
export const text = 'Hello World'// anotherfile.js
import { text } from 'somefile.js'console.log(text)
CommonJS vs ES6 Modules
อธิบายพื้นฐานกันไปแล้ว จริงๆหลายๆคนน่าจะรู้จักและเคยใช้กันมาบ้าง เรามาว่าถึงความแตกต่างและข้อควรระวังกันดีกว่า ผมคิดว่าหลายๆท่านอาจจะไม่เคยทราบมาก่อนก็ได้นะ มาลองดูตัวอย่างกัน
ลองเขียน JS แบบ CommonJS กันก่อน
// lib.js
var number = 0
function increaseNumber() {
number++
}
module.exports = {
number: number,
increaseNumber: increaseNumber
}
ทีนี้มาลองเขียน Code ที่ทำงานเหมือนกันในแบบ ES6 Modules บ้าง
// lib.js
export let number = 0
export function increaseNumber() {
number++
}
เพื่อนๆคิดว่า การเขียน Code ด้วย 2 วิธีข้างต้นนั้น ให้ผลลัพธ์แตกต่างกันหรือไม่ ??
ผมให้เวลาคิดสัก 2–3 นาที… ติ๊ก ต่อก .. ติ๊กกก …. ต่อก ..
….
..
…….. มาลองไปด้วยกันทั้ง 2 วิธีกันโลดดด
CommonJS Example Usage
มาลองใช้งานแบบ CommonJS กันก่อน
var libs = require('./lib')console.log(libs.number) // = 0
libs.increaseNumber()
console.log(libs.number) // = 0
จากตัวอย่างข้างต้น จะเห็นว่าค่า number นั้นไม่ได้ถูกเพิ่มค่าขึ้นด้วยฟังก์ชัน increaseNumber ! แต่ถ้าเราอยากให้มันเพิ่มล่ะต้องเขียนยังไง ?
var libs = require('./lib')console.log(libs.number) // = 0
libs.number++
console.log(libs.number) // = 1
การ Import ของ CommonJS นั้นจะเป็นการ Copy Object ออกมาใช้งาน ทำให้ค่า number นั้นถูก copy ออกมาด้วย ดังนั้นการเรียกใช้งาน function increaseNumber ไม่ส่งผลต่อค่า number ที่ถูก copy ออกมาแล้วนั่นเอง แต่อย่างไรก็ตาม libs.number ที่ถูก Copy ออกมานั้นยังคงเปลี่ยนแปลงค่าได้เสมอ ทำให้การบวกค่าเข้าไปตรงๆด้วยคำสั่ง libs.number++ ทำให้ค่าเปลี่ยนไป
ES6 Modules Example Usage
ในทางกลับกัน ES6 Modules นั้นทำงานต่างจาก CommonJS โดยสิ้นเชิง แต่ก่อนอื่นมาดูวิธีเรียกใช้กันก่อน
import { number, increaseNumber } from './lib'
console.log(number) // 0
increaseNumber()
console.log(number) // 1
จะเห็นว่าผลลัพธ์การเรียกใช้ของทั้ง 2 วิธีนั้นแตกต่างกันเพราะ ES6 Modules นั้นทำงานแบบ import bindings ดังนั้นค่าตัวแปรจึง references ถึงกันเสมอ ทำให้มีความอันตรายอยู่ หากใช้อย่างไม่ระวังหรือไม่เข้าใจมันดีพอ
จริงๆการ Bindings ของ ES6 Modules นั้นทำมาแก้ปัญหา “Cyclic Dependencies” ของ CommonJS แต่ก็ไม่ได้แก้ปัญหาได้ 100% เสียทีเดียว หากสนใจก็ไปอ่านเพิ่มเติมได้ทื่ References เลยครับ เพราะมันค่อนข้างละเอียดพอสมควร
สรุป
ไม่ว่าจะเขียนด้วยวิธีไหนสิ่งที่ต้องคำนึงถึงมากที่สุดคือการเข้าใจในสิ่งที่เราเขียน เพราะจะทำให้ระบบนั้นมี Bugs น้อยที่สุดเท่าที่จะเป็นไปได้ หรือถ้าเกิดมี Bugs ขึ้นมาเราก็จะรู้ว่าเกิดขึ้นเพราะอะไรและสามารถแก้ไขได้ทันท่วงทีครับผม
สุดท้ายก็มีข้อควรระวังนิดหน่อยสำหรับคนที่อยากจะไปลองเล่นดู คือการใช้งานบน Browser อาจจะต้อง setup Babel/Webpack มาช่วย หรือถ้าอยากลองเล่นด้วย Node.js ตัว ES6 Modules นั้นจะต้องใช้ Node version 12+ และต้องเขียนเป็น .mjs หรือระบุ type module ใน package.json ด้วย และต้องใส่ option — experimental-modules เพิ่มตอนจะ run อีก โอย… ยุ่งยากเป็นบ้า
แต่…. ถ้าใครขี้เกียจก็เข้าไปดู Code ตัวอย่างของผมผมได้ ที่นี่