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

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

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

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

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

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

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

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

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

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

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

หากต้องการดูว่า Next.js รวมแอปเข้าด้วยกันอย่างไร ให้ตรวจสอบการติดตามเครือข่ายในเครื่องมือสำหรับนักพัฒนาเว็บ

  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. ในรายการแบบเลื่อนลงการควบคุม ให้เลือก 3G ที่รวดเร็ว

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

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

หน้าจอมืดพร้อมข้อความ

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

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

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

บทสรุป

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