การแยกโค้ดด้วยการนำเข้าแบบไดนามิกใน Next.js

วิธีเร่งความเร็วแอป Next.js ด้วยกลยุทธ์การแยกโค้ดและการโหลดอย่างชาญฉลาด

โพสต์นี้จะอธิบายการแยกโค้ดประเภทต่างๆ และวิธีใช้การนําเข้าแบบไดนามิกเพื่อเพิ่มความเร็วของแอป Next.js

การแยกโค้ดตามเส้นทางและตามคอมโพเนนต์

โดยค่าเริ่มต้น Next.js จะแยก JavaScript ออกเป็นกลุ่มแยกกันสำหรับแต่ละเส้นทาง เมื่อผู้ใช้โหลดแอปพลิเคชัน Next.js จะส่งเฉพาะโค้ดที่จําเป็นสําหรับเส้นทางเริ่มต้นเท่านั้น เมื่อผู้ใช้ไปยังส่วนต่างๆ ของแอปพลิเคชัน ระบบจะดึงข้อมูลกลุ่มที่เชื่อมโยงกับเส้นทางอื่นๆ การแยกโค้ดตามเส้นทางจะลดจํานวนสคริปต์ที่ต้องแยกวิเคราะห์และคอมไพล์พร้อมกัน ซึ่งส่งผลให้หน้าเว็บโหลดเร็วขึ้น

แม้ว่าการแยกโค้ดตามเส้นทางจะเป็นค่าเริ่มต้นที่ดี แต่คุณก็เพิ่มประสิทธิภาพกระบวนการโหลดเพิ่มเติมได้ด้วยการแยกโค้ดที่ระดับคอมโพเนนต์ หากคอมโพเนนต์ในแอปมีขนาดใหญ่ คุณควรแยกคอมโพเนนต์เหล่านั้นออกเป็นส่วนๆ วิธีนี้ช่วยให้โหลดคอมโพเนนต์ขนาดใหญ่ที่ไม่สําคัญหรือแสดงผลเฉพาะเมื่อผู้ใช้โต้ตอบบางอย่าง (เช่น การคลิกปุ่ม) แบบ Lazy Load ได้

Next.js รองรับ import() แบบไดนามิก ซึ่งช่วยให้คุณนําเข้าโมดูล JavaScript (รวมถึงคอมโพเนนต์ React) แบบไดนามิกและโหลดการนําเข้าแต่ละรายการเป็นกลุ่มแยกกันได้ วิธีนี้ช่วยให้คุณแบ่งโค้ดระดับคอมโพเนนต์และควบคุมการโหลดทรัพยากรได้ เพื่อให้ผู้ใช้ดาวน์โหลดเฉพาะโค้ดที่ต้องการสำหรับส่วนในเว็บไซต์ที่กําลังดู ใน Next.js คอมโพเนนต์เหล่านี้จะแสดงผลฝั่งเซิร์ฟเวอร์ (SSR) โดยค่าเริ่มต้น

การนําเข้าแบบไดนามิกที่ใช้งานจริง

โพสต์นี้มีตัวอย่างแอปหลายเวอร์ชันที่ประกอบด้วยหน้าเว็บง่ายๆ ที่มีปุ่มเดียว เมื่อคลิกปุ่ม คุณจะเห็นลูกสุนัขน่ารัก เมื่อเลื่อนดูแอปแต่ละเวอร์ชัน คุณจะเห็นความแตกต่างระหว่างการนําเข้าแบบไดนามิกกับการนําเข้าแบบคงที่ และวิธีใช้งาน

ในแอปเวอร์ชันแรก ลูกสุนัขอาศัยอยู่ใน components/Puppy.js หากต้องการแสดงลูกสุนัขบนหน้าเว็บ แอปจะนําเข้าคอมโพเนนต์ Puppy ใน index.js ด้วยคำสั่งนําเข้าแบบคงที่ ดังนี้

import Puppy from "../components/Puppy";

หากต้องการดูวิธีที่ Next.js รวมแอป ให้ตรวจสอบการติดตามเครือข่ายใน DevTools โดยทำดังนี้

  1. หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ เต็มหน้าจอ

  2. กดแป้น Control+Shift+J (หรือ Command+Option+J ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์

  3. คลิกแท็บเครือข่าย

  4. เลือกช่องทำเครื่องหมายปิดใช้แคช

  5. โหลดหน้าเว็บซ้ำ

เมื่อคุณโหลดหน้าเว็บ โค้ดที่จำเป็นทั้งหมด รวมถึงคอมโพเนนต์ Puppy.js จะรวมอยู่ใน index.js

แท็บเครือข่ายของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์แสดงไฟล์ JavaScript 6 ไฟล์ ได้แก่ index.js, app.js, webpack.js, main.js, 0.js และไฟล์ dll (Dynamic-Link Library)

เมื่อกดปุ่ม Click me ระบบจะเพิ่มเฉพาะคำขอ JPEG ของสุนัขลงในแท็บเครือข่าย

แท็บเครือข่ายของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์หลังจากคลิกปุ่ม ซึ่งแสดงไฟล์ JavaScript 6 ไฟล์และรูปภาพ 1 รูปเดียวกัน

ข้อเสียของแนวทางนี้คือ แม้ว่าผู้ใช้จะไม่คลิกปุ่มเพื่อดูลูกสุนัข แต่ก็ต้องโหลดคอมโพเนนต์ Puppy เนื่องจากรวมอยู่ใน index.js ในตัวอย่างนี้ การดำเนินการนี้อาจไม่ใช่เรื่องใหญ่ แต่ในแอปพลิเคชันในชีวิตจริง มักจะเป็นการปรับปรุงที่ยิ่งใหญ่ในการโหลดคอมโพเนนต์ขนาดใหญ่เฉพาะเมื่อจำเป็นเท่านั้น

ตอนนี้มาดูแอปเวอร์ชันที่ 2 ซึ่งมีการนําเข้าแบบไดนามิกแทนที่การนําเข้าแบบคงที่ Next.js มี next/dynamic ซึ่งทำให้นําเข้าแบบไดนามิกสําหรับคอมโพเนนต์ใดก็ได้ใน Next ได้

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

ทำตามขั้นตอนจากตัวอย่างแรกเพื่อตรวจสอบการติดตามเครือข่าย

เมื่อคุณโหลดแอปครั้งแรก ระบบจะดาวน์โหลดเฉพาะ index.js เท่านั้น ครั้งนี้มีขนาดเล็กลง 0.5 KB (จาก 37.9 KB เหลือ 37.4 KB) เนื่องจากไม่มีโค้ดสำหรับคอมโพเนนต์ Puppy ดังนี้

เครือข่ายของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์แสดงไฟล์ JavaScript 6 ไฟล์เดียวกัน ยกเว้น index.js ที่ตอนนี้มีขนาดเล็กลง 0.5 KB

ตอนนี้คอมโพเนนต์ Puppy อยู่ในกลุ่มแยกต่างหาก 1.js ซึ่งจะโหลดก็ต่อเมื่อคุณกดปุ่มเท่านั้น

แท็บเครือข่ายของเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์หลังจากคลิกปุ่ม ซึ่งแสดงไฟล์ 1.js เพิ่มเติมและรูปภาพที่เพิ่มไว้ที่ด้านล่างของรายการไฟล์

ในแอปพลิเคชันในชีวิตจริง คอมโพเนนต์มักจะมีขนาดใหญ่กว่ามาก และการโหลดแบบเลื่อนเวลาสามารถลดเพย์โหลด JavaScript เริ่มต้นได้หลายร้อยกิโลไบต์

การนําเข้าแบบไดนามิกที่มีตัวบ่งชี้การโหลดที่กําหนดเอง

เมื่อใช้การโหลดแบบ Lazy Loading คุณควรระบุตัวบ่งชี้การโหลดไว้เผื่อในกรณีที่เกิดความล่าช้า ใน Next.js คุณทําได้โดยระบุอาร์กิวเมนต์เพิ่มเติมให้กับฟังก์ชัน dynamic() ดังนี้

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading
: () => <p>Loading...</p>
});

หากต้องการดูการทำงานของตัวบ่งชี้การโหลด ให้จําลองการเชื่อมต่อเครือข่ายที่ช้าใน DevTools โดยทําดังนี้

  1. หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ เต็มหน้าจอ

  2. กดแป้น Control+Shift+J (หรือ Command+Option+J ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์

  3. คลิกแท็บเครือข่าย

  4. เลือกช่องทำเครื่องหมายปิดใช้แคช

  5. ในรายการแบบเลื่อนลงการจำกัด ให้เลือก Fast 3G

  6. กดปุ่มคลิกฉัน

ตอนนี้เมื่อคลิกปุ่ม ระบบจะใช้เวลาสักครู่ในการโหลดคอมโพเนนต์และแอปจะแสดงข้อความ "กำลังโหลด…" ในระหว่างนี้

หน้าจอสีดําพร้อมข้อความ

การนําเข้าแบบไดนามิกที่ไม่มี SSR

หากต้องการแสดงผลคอมโพเนนต์ฝั่งไคลเอ็นต์เท่านั้น (เช่น วิดเจ็ตแชท) ให้ตั้งค่าตัวเลือก ssr เป็น false ดังนี้

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr
: false,
});

บทสรุป

การสนับสนุนการนําเข้าแบบไดนามิกช่วยให้ Next.js แยกโค้ดระดับคอมโพเนนต์ได้ ซึ่งจะช่วยลดเพย์โหลด JavaScript และปรับปรุงเวลาในการโหลดแอปพลิเคชัน คอมโพเนนต์ทั้งหมดจะแสดงผลฝั่งเซิร์ฟเวอร์โดยค่าเริ่มต้น และคุณปิดใช้ตัวเลือกนี้ได้เมื่อใดก็ตามที่ต้องการ