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

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

สิ่งที่คุณจะได้เรียนรู้

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

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

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

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

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 (ไลบรารีลิงก์แบบไดนามิก)

เมื่อคุณกดปุ่มคลิกฉัน ระบบจะเพิ่มเฉพาะคำขอ 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 เพิ่มเติมและรูปภาพที่เพิ่มไว้ที่ด้านล่างของรายการไฟล์

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

การนําเข้าแบบไดนามิกที่มีสัญญาณบอกสถานะการโหลดที่กําหนดเอง

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

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

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

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

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

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

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

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

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

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

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

การนำเข้าแบบไดนามิกโดยไม่มี SSR

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

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

บทสรุป

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