นักพัฒนามักจะประเมินความยากของ PDF viewer ต่ำไปเสมอ มันดูเหมือนปัญหา "file-in-a-box" ง่ายๆ แค่คุณดึง bytes มา ชี้ component ไปที่ URL แล้วก็ถือว่าเสร็จสิ้น Vue.js ทำให้งาน UI ส่วนใหญ่ดูเรียบง่ายแบบนี้ แต่ PDF viewer ที่พร้อมใช้งานจริง (production-ready) กลับเป็นหนึ่งในฟีเจอร์ที่ค่อยๆ ขยายขอบเขตจนกินเวลาไปทั้ง sprint ผมลองสร้าง viewer ตัวเดิมด้วย 4 วิธีที่แตกต่างกันภายในสองสัปดาห์ และทุกความพยายามตอกย้ำจุดเดิมว่า: ไลบรารีที่คุณเลือกในวันจันทร์ จะเป็นตัวกำหนดว่าคุณจะต้องตามแก้บั๊กตัวไหนในอีกสองเดือนข้างหน้า

The Outdated Tutorial Trap

คู่มือส่วนใหญ่ยังคงแนะนำไลบรารีที่อัปเดตครั้งล่าสุดตั้งแต่สมัยที่ Vue 2 ยังเป็นมาตรฐาน นักพัฒนาอ่าน README ผ่านๆ รันคำสั่งติดตั้ง และคิดว่าส่วนที่ยากที่สุดจบลงแล้ว แต่ในความเป็นจริง ส่วนที่ยากเพิ่งจะเริ่มต้นขึ้นเท่านั้น คุณจะต้องมีระบบค้นหา คุณจะต้องจัดการกับเอกสารนโยบายที่มีความยาวถึง 400 หน้าโดยไม่ทำให้แท็บเบราว์เซอร์ค้าง ผู้ใช้งานบนมือถือจะถามว่าทำไมการใช้สองนิ้วซูม (pinch-to-zoom) ถึงดูติดขัด README แทบไม่เคยเตือนเรื่องเหล่านี้เลย เพราะตัวอย่าง (demo) มักจะแสดงผลแค่หน้าแรกของบทความวิชาการที่มีเพียงห้าหน้าเท่านั้น

The Four Approaches

ไม่มีไลบรารี PDF ตัวไหนที่ดีที่สุดสำหรับ Vue มีเพียงตัวที่ "เหมาะสมที่สุด" กับสิ่งที่ผู้ใช้ของคุณต้องการทำจริงๆ เท่านั้น

PDF.js: The DIY Path

PDF.js ของ Mozilla คือเอนจินที่อยู่เบื้องหลัง viewer บนเว็บเกือบทุกตัว การนำมันมาใช้ในแอป Vue 3 ไม่ใช่แค่การติดตั้ง component แต่มันคือการรับโปรเจกต์หนึ่งมาดูแล คุณต้องดึงเอกสารด้วย getDocument เรนเดอร์แต่ละหน้าลงใน element <canvas> และเชื่อมต่อ canvas เหล่านั้นเข้ากับ template ของคุณ วันแรกคุณจะรู้สึกว่างานเดินหน้าไปได้ดี แต่พอถึงวันที่สาม คุณจะเริ่มวุ่นอยู่กับการตั้งค่า worker script ให้ทำงานร่วมกับ Vite bundling และ CORS headers ได้อย่างราบรื่น

การเลื่อน (scroll) แบบ native ของเบราว์เซอร์ทำงานได้ดีกับเอกสารสิบหน้า แต่จะเริ่มค้างเมื่อเจอพันหน้า คุณจึงต้องสร้าง virtual scrolling ขึ้นมาเอง จากนั้นคุณจะพบว่าไม่สามารถเลือกข้อความได้ คุณจึงต้องวาง transparent text divs ทับลงบนแต่ละ canvas การสั่งพิมพ์ดูเบลอ คุณจึงต้องไปไล่แก้เรื่อง DPI settings และ media queries การ pinch-to-zoom บนมือถือจะไปตีกับระบบ gesture handling ของเบราว์เซอร์ การค้นหาข้ามเอกสารหมายถึงการดึงข้อมูล (extracting) และทำดัชนี (indexing) ข้อความในทุกหน้าแบบ asynchronous จากนั้นจึงสร้าง UI ที่จัดการคิวผลลัพธ์โดยไม่ไปขัดขวาง (blocking) main thread แม้จะมี AI coding assistant อย่าง Cursor ช่วยเขียน boilerplate ให้ แต่คุณก็ยังต้องเป็นคนดูแลโครงสร้าง (architecture) เอง ส่วนที่ยากไม่ได้หายไปไหน แต่มันย้ายเข้ามาอยู่ใน codebase ของคุณแทน วิธีนี้จะสมเหตุสมผลก็ต่อเมื่อความต้องการของคุณเฉพาะเจาะจงจริงๆ หรือเมื่อคุณมีเวลาว่างหลายสัปดาห์และมีเหตุผลจำเป็นที่ต้องหลีกเลี่ยงพฤติกรรมแบบสำเร็จรูป (off-the-shelf)

vue-pdf-embed: The Lightweight Path

บางครั้งสิ่งที่คุณต้องการก็แค่การแสดงไฟล์ vue-pdf-embed คือ component สำหรับ Vue 3 ที่รับ source และเรนเดอร์หน้าต่างๆ เรียงกันในแนวตั้ง การติดตั้งและรวมเข้ากับโปรเจกต์ใช้เวลาเพียงไม่กี่นาที สำหรับ admin panel ภายในที่ใช้แสดงใบแจ้งหนี้หรือรายงานการปฏิบัติตามกฎระเบียบ (compliance reports) วิธีนี้มักจะเพียงพอแล้ว ตัว component จะจัดการเรื่องการเรนเดอร์ canvas ให้ และผู้ใช้ก็สามารถเลื่อนดูได้

สิ่งที่ต้องแลกมาคือฟีเจอร์อื่นๆ ทั้งหมด มันไม่มี toolbar, ไม่มีระบบค้นหาเอกสาร, ไม่มีแถบข้างสำหรับ thumbnail และไม่มีการนำทางหน้า (page navigation) นอกเหนือจากการเลื่อนผ่าน scroll container ทันทีที่ผู้มีส่วนได้ส่วนเสีย (stakeholder) ถามว่า "ฉันค้นหาเลขที่ใบแจ้งหนี้ได้ไหม?" งานที่ควรจะเสร็จในสองชั่วโมงก็จะบานปลายกลายเป็นการสร้างระบบขึ้นมาใหม่เองทั้งหมด เลือกวิธีนี้เมื่อ PDF ของคุณมีจำนวนหน้าไม่มาก ผู้ใช้งานเป็นคนภายในองค์กร และรูปแบบการใช้งานเป็นเพียงการเลื่อนอ่าน (read-only scrolling) เท่านั้น

@tato30/vue-pdf: The Control Path

ไลบรารีนี้เปลี่ยนรูปแบบจาก monolithic component มาเป็น composable โดยจะเปิดให้ใช้ usePDF ซึ่งคุณสามารถเรียกใช้ภายใน setup block ได้ แทนที่จะเรนเดอร์เอกสารทั้งฉบับที่เลื่อนได้ คุณจะจัดการทีละหน้าผ่าน reactive refs ฟังดูเหมือนเป็นงานที่เพิ่มขึ้น แต่จะช่วยให้คุณทำงานได้อิสระมากขึ้นเมื่อ interface ต้องการความแม่นยำสูง

ลองนึกถึงเครื่องมือตรวจสอบการเคลมประกันที่เจ้าหน้าที่ต้องตรวจสอบเอกสารทีละหน้า คลิก Next แล้วระบบจะบันทึกเหตุการณ์การเข้าดูแต่ละครั้ง การใช้ viewer แบบเลื่อนต่อเนื่อง (continuous scrollable viewer) จะไม่ใช่แนวทางที่เหมาะสมในกรณีนี้ คุณต้องการตัวเปลี่ยนหน้า (paginator) ที่ควบคุมได้ อาจจะมีระบบคอมเมนต์ระดับหน้า หรือปุ่มอนุมัติที่ผูกติดกับดัชนีหน้าปัจจุบันโดยตรง เนื่องจาก usePDF ส่งค่าจำนวนหน้าและหน้าปัจจุบันมาให้ในรูปแบบ reactive data การนำไปเชื่อมต่อกับ custom navigation bar หรือ progress indicator จึงทำได้อย่างเป็นธรรมชาติ คุณยังคงต้องสร้างส่วนควบคุม (chrome) รอบๆ canvas เอง แต่คุณจะรอดพ้นจากงานเขียน boilerplate ในระดับการเรนเดอร์ที่ต่ำที่สุด วิธีนี้เหมาะกับแอปที่ผู้ใช้ต้องดูทีละหน้า แทนที่จะเป็นการกวาดสายตาอ่านต้นฉบับทั้งฉบับ

Vue PDF Viewer: The Full-Service Path

ถึงจุดหนึ่งที่การสร้างฟีเจอร์ของตัว Viewer ขึ้นมาใหม่จะกลายเป็นสิ่งที่ดึงความสนใจไปจากผลิตภัณฑ์หลักของคุณ Vue PDF Viewer เป็นคอมโพเนนต์เชิงพาณิชย์ที่มาพร้อมกับแถบเครื่องมือแบบครบชุด, การค้นหาข้อความ, การเขียนคำอธิบาย (annotations), การรองรับการใช้งานบนมือถือ (mobile responsiveness) และการทำ virtual scrolling ที่ผ่านการทดสอบกรณีขอบเขต (edge cases) มาแล้ว หน้าที่ของคุณจะเปลี่ยนจากการคิดค้นเป็นการตั้งค่า (configuration) แทน คุณเพียงแค่ปรับแต่งธีมให้เข้ากับระบบดีไซน์ (design system) ของคุณ, เปิด/ปิดฟีเจอร์ที่ต้องการ และไปโฟกัสกับงานที่สร้างความแตกต่างให้กับแอปของคุณจริงๆ

ค่าธรรมเนียมใบอนุญาต (license fee) ที่ต้องจ่ายล่วงหน้านั้นเป็นเรื่องจริง แต่ค่าเสียเวลาของวิศวกรสองสัปดาห์ที่ต้องใช้ในการสร้างระบบดัชนีการค้นหา (search indexing) และเลเยอร์การเขียนคำอธิบายขึ้นมาใหม่ก็เป็นเรื่องจริงเช่นกัน นี่คือเครื่องมือที่เหมาะสมเมื่อคุณกำลังจะปล่อยแอปพลิเคชันใช้งานจริง (production app) ภายใต้กำหนดการที่เร่งด่วน และผู้ใช้งานของคุณคาดหวังประสบการณ์การใช้งานที่เทียบเท่ากับซอฟต์แวร์ PDF บนเดสก์ท็อป

ต้นทุนที่แท้จริงของการติดตั้ง

ข้อผิดพลาดที่ใหญ่ที่สุดคือการมองว่าคำสั่ง npm install คือราคาทั้งหมด ต้นทุนที่แท้จริงคือสิ่งที่คุณต้องสร้างหลังจากติดตั้งเสร็จสิ้น ไลบรารีที่มีน้ำหนักเบา (lightweight library) อาจจะมีราคาถูกในวันแรก แต่จะกลายเป็นราคาแพงในวันที่ยี่สิบเมื่อคุณพบว่าคุณต้องการแถบค้นหา ส่วนแนวทางแบบ DIY นั้นฟรีในวันแรก แต่จะกลายเป็นราคาแพงในวันที่หกสิบเมื่อคุณยังคงต้องตามแก้เรื่องพื้นที่สัมผัสบนมือถือ (mobile touch targets) และสไตล์ชีตสำหรับการพิมพ์ (print stylesheets) แนวทางเชิงพาณิชย์อาจต้องเสียเงินล่วงหน้า แต่สามารถช่วยประหยัดเวลาของวิศวกรได้หลายสัปดาห์ ซึ่งคุณสามารถนำเวลานั้นไปใช้กับตรรกะทางธุรกิจ (business logic) ที่สำคัญกว่าได้

อย่าเลือกไลบรารีเพียงเพราะ README ของมันสั้นและอ่านง่าย แต่จงเลือกตามความต้องการของโปรเจกต์ของคุณ หากคุณต้องการแถบด้านข้างที่เป็นรูปย่อ (thumbnails), การค้นหาข้อความ และการเขียนคำอธิบายฝั่งไคลเอนต์ (client-side annotations) นั่นหมายความว่าคุณควรไปทางโซลูชันแบบครบวงจร (full-service solution) แต่ถ้าคุณต้องการเพียงแค่การแสดงตัวอย่างใบเสร็จแบบเร็วๆ ภายในแดชบอร์ดภายในองค์กร นั่นหมายความว่าคุณควรเลือกแบบ lightweight embed

บทสรุปที่แท้จริง

ก่อนที่คุณจะตัดสินใจเลือกใช้ไลบรารี Vue PDF ใดๆ ให้จดบันทึกสิ่งที่ผู้ใช้งานของคุณต้องทำอย่างชัดเจน หากพวกเขาแค่ต้องการเลื่อนดูเอกสารสั้นๆ vue-pdf-embed ก็เพียงพอสำหรับคุณแล้ว หากพวกเขาต้องดูทีละหน้าตามขั้นตอนการทำงาน (workflow) ที่กำหนดไว้ ให้เลือกใช้ @tato30/vue-pdf หากพวกเขาต้องเขียนคำอธิบาย, ค้นหา และสั่งพิมพ์ภายในแอปพลิเคชันที่มีความสำคัญต่อธุรกิจ ให้ซื้อตัว viewer เชิงพาณิชย์ และหากความต้องการของคุณมีความเฉพาะตัวสูงมากแต่มีเวลาที่ยืดหยุ่น ให้เผื่อเวลาไว้หลายสัปดาห์แล้วสร้างขึ้นโดยใช้ PDF.js โดยตรง ตัว PDF viewer ไม่ใช่แค่ไฟล์ที่อยู่ในกล่อง แต่มันคืออินเทอร์เฟซสำหรับจัดการเอกสารแบบเต็มรูปแบบ และการเลือกองค์ประกอบพื้นฐานของคุณจะเป็นตัวกำหนดว่าคุณจะสามารถปล่อยผลิตภัณฑ์ได้ภายในเดือนนี้หรือไตรมาสหน้า