PlusMagi's Blog By Pitt Phunsanit Uncategorized ชิอิคาวะ (Chiikawa) หรือ นันกะ ชิอิซาคุเตะ คาวาอิ ยัตสึ (Nanka Chiisakute Kawaii Yatsu)

ชิอิคาวะ (Chiikawa) หรือ นันกะ ชิอิซาคุเตะ คาวาอิ ยัตสึ (Nanka Chiisakute Kawaii Yatsu)

ในการพัฒนาเว็บแอปพลิเคชันยุคปัจจุบัน นักพัฒนาต่างต้องการหยิบใช้ฟีเจอร์ใหม่ ๆ ทั้งจากมาตรฐาน ECMAScript (JavaScript ล่าสุด), Web APIs ใหม่ๆ หรือแม้แต่ CSS ปรับปรุงใหม่อย่าง Customizable Select แต่ความจริงอันเจ็บปวดคือ “เบราว์เซอร์ของผู้ใช้ไม่ได้อัปเดตพร้อมกันทุกคน”

ผู้ใช้บางคนอาจยังติดอยู่บนระบบปฏิบัติการรุ่นเก่า ใช้ Safari เวอร์ชันก่อนหน้า หรือเปิดเว็บผ่าน Webview ภายในแอปพลิเคชันที่ไม่ได้ใช้เอนจินล่าสุด ปัญหานี้จึงนำไปสู่เครื่องมือสำคัญที่นักพัฒนาสาย Frontend ต้องรู้จักดี นั่นคือ Polyfill


Polyfill คืออะไร?

Polyfill คือชุดโค้ด (ส่วนใหญ่เขียนด้วย JavaScript) ที่ถูกสร้างขึ้นมาเพื่อ “จำลอง” หรือเลียนแบบการทำงานของ Web API หรือฟังก์ชันมาตรฐานใหม่ๆ ให้กับเบราว์เซอร์ที่ยังไม่รองรับฟีเจอร์นั้น ๆ

คำนี้มีที่มาจาก Polyfilla ซึ่งเป็นชื่อแบรนด์ปูนยาแนวหรือวัสดุอุดรอยรั่วบนกำแพงในประเทศอังกฤษ โดย Remy Sharp (นักพัฒนาเว็บชื่อดัง) ได้นำคำนี้มาเปรียบเปรยในปี 2009 ว่า

“ถ้ากำแพงห้องมีรอยแตกหรือรูโหว่ เราก็เอา Polyfilla มาอุดให้มันเรียบเนียน เช่นเดียวกับเบราว์เซอร์ ถ้าเบราว์เซอร์ไหนขาด API มาตรฐานตัวใดไป เราก็เอา Polyfill อุดเข้าไป เพื่อให้นักพัฒนาสามารถเขียนโค้ดมาตรฐานชุดเดียวกันได้โดยหน้าเว็บไม่พัง”


Polyfill ทำงานอย่างไร?

หลักการทำงานของ Polyfill มักจะเดินตามแนวทาง Feature Detection (ตรวจจับก่อนว่ามีของไหม) เสมอ โดยจะไม่ไปเขียนทับฟังก์ชันที่มีอยู่แล้วของเบราว์เซอร์เด็ดขาด

ตัวอย่างเช่น ฟังก์ชัน Array.prototype.includes ซึ่งเพิ่มเข้ามาใน ES2016

// ตรวจสอบว่าเบราว์เซอร์มีฟังก์ชัน includes อยู่ใน Array แล้วหรือยัง
if (!Array.prototype.includes) {
  // ถ้ายังไม่มี (เบราว์เซอร์เก่า) ให้สร้างฟังก์ชันนี้ขึ้นมาทดแทน
  Array.prototype.includes = function(searchElement, fromIndex) {
    'use strict';
    var O = Object(this);
    var len = parseInt(O.length, 10) || 0;
    if (len === 0) return false;
    var n = parseInt(fromIndex, 10) || 0;
    var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
    while (k < len) {
      if (O[k] === searchElement) return true;
      k++;
    }
    return false;
  };
}

ถ้าผู้ใช้เปิดด้วย Chrome หรือ Safari รุ่นใหม่ โค้ดด้านบนจะไม่ทำอะไรเลย แต่ถ้าผู้ใช้เปิดด้วยเบราว์เซอร์เก่า โค้ดชุดนี้จะเข้าไปอุดช่องโหว่ทันที ทำให้นักพัฒนาเรียกใช้ [1, 2, 3].includes(2) ได้อย่างสบายใจ


Polyfill ต่างจาก Transpiler (Babel) อย่างไร?

หลายคนมักสับสนระหว่างหน้าที่ของ Polyfill กับ Transpiler (เช่น Babel หรือ SWC)

  • Transpiler (แปลง Syntax): รับหน้าที่แปลงไวยากรณ์ของภาษา เช่น แปลง Arrow Function (() => {}), Optional Chaining (a?.b), หรือ Classes ให้กลายเป็นไวยากรณ์แบบดั้งเดิมอย่าง function และ prototype
  • Polyfill (เติม API และ Object): ทำหน้าที่สร้าง Object, Method หรือ API ในระดับ Runtime ที่ไวยากรณ์พื้นฐานสร้างขึ้นมาเองไม่ได้ เช่น Promise, fetch(), Map/Set, หรือ IntersectionObserver

ในการใช้งานจริง เราจึงมักใช้คู่กันเสมอ (Transpile โค้ดให้ Syntax รันได้ + โหลด Polyfill ให้มี API ใช้งาน)


Polyfill สำหรับ UI / CSS มีหน้าตาเป็นอย่างไร?

เมื่อพูดถึงฟีเจอร์อย่าง Customizable <select> (appearance: base-select) หรือ CSS Nesting ยุคใหม่ Polyfill ฝั่ง UI มักจะไม่ได้อุดแค่ JavaScript เพียวๆ แต่จะมาในรูปแบบของ Web Components หรือ DOM Manipulation Scripts

  1. สแกนหา Element
    ตรวจจับว่าเครื่องลูกข่ายรองรับ CSS API นั้นหรือไม่ เช่น CSS.supports('appearance', 'base-select')
  2. สร้าง Custom Markup
    ถ้าไม่รองรับ Script จะดึง native <select> เดิมไปซ่อน แล้วสร้างชุด <div> พร้อมแปะ WAI-ARIA (role="listbox", aria-expanded) ขึ้นมาครอบแทน
  3. ซิงก์ค่ากลับ
    เมื่อผู้ใช้คลิกเลือก Option จำลอง ตัว Polyfill จะอัปเดต value และสั่ง dispatch event change กลับไปยัง native element ด้านหลัง เพื่อให้ฟอร์มทำงานได้ปกติ

ข้อควรระวังและแนวทางปฏิบัติ (Best Practices)

แม้ Polyfill จะมีประโยชน์มหาศาล แต่การใช้อย่างไม่ระวังอาจสร้างผลเสียต่อประสิทธิภาพเว็บได้

1. หลีกเลี่ยงการ Bundle Polyfill ก้อนโตส่งให้ทุกคน
การยัด Polyfill ทุกอย่างลงใน main bundle จะทำให้ขนาดไฟล์ใหญ่ขึ้นโดยไม่จำเป็น ผู้ใช้เบราว์เซอร์รุ่นใหม่ (ซึ่งเป็นคนส่วนใหญ่) ต้องเสียเวลาโหลดและประมวลผลโค้ดที่ตัวเองไม่ได้ใช้

2. ใช้ Dynamic Import ตามเงื่อนไข
ควรใช้วิธีโหลดแบบ On-demand เมื่อตรวจสอบแล้วว่าขาด API นั้นจริง ๆ

async function prepareApp() {
  if (!('IntersectionObserver' in window)) {
    // โหลด Polyfill เฉพาะเมื่อเบราว์เซอร์ไม่รู้จัก IntersectionObserver
    await import('intersection-observer');
  }
  // เริ่มต้นรันโค้ดหลักของแอป
  initializeApp();
}

3. ตรวจสอบ Performance และ Memory Leak
Polyfill ที่พยายามจำลอง Native UI มักมีการดักจับ Event คลิก การคำนวณตำแหน่งพิกัด หรือการ Observe หน้าจอ ซึ่งกินทรัพยากร CPU และแบตเตอรี่มากกว่าสิ่งที่ OS Render เองโดยกำเนิด


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


Exit mobile version