วิธีเพิ่มความเร็วของแอป 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 รวมแอปอย่างไร ให้ตรวจสอบการติดตามเครือข่ายในเครื่องมือสำหรับนักพัฒนาเว็บ โดยทำดังนี้
หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ
กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
คลิกแท็บเครือข่าย
เลือกช่องทำเครื่องหมายปิดใช้แคช
โหลดหน้าเว็บซ้ำ
เมื่อโหลดหน้าเว็บ โค้ดที่จำเป็นทั้งหมดซึ่งรวมถึงคอมโพเนนต์ Puppy.js
จะรวมกลุ่มอยู่ใน index.js
ดังนี้
![แท็บเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บแสดงไฟล์ JavaScript 6 ไฟล์ ได้แก่ index.js, app.js, webpack.js, main.js, 0.js และไฟล์ dll (ไลบรารีลิงก์แบบไดนามิก)](https://web.developers.google.cn/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-show-c3449cc648185.png?authuser=1&hl=th)
เมื่อคุณกดปุ่มคลิกฉัน ระบบจะเพิ่มเฉพาะคำขอ JPEG สำหรับลูกสุนัขลงในแท็บเครือข่าย ดังนี้
![แท็บเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บหลังจากคลิกปุ่ม ซึ่งแสดงไฟล์ JavaScript 6 ไฟล์และรูปภาพ 1 รูปเดียวกัน](https://web.developers.google.cn/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-the-94b209c5e4e56.png?authuser=1&hl=th)
ข้อเสียของวิธีนี้คือแม้ว่าผู้ใช้จะไม่คลิกปุ่มเพื่อดูลูกสุนัข พวกเขาจะต้องโหลดคอมโพเนนต์ 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](https://web.developers.google.cn/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-showing-8cbd4309d29c5.png?authuser=1&hl=th)
ตอนนี้คอมโพเนนต์ Puppy
จะอยู่เป็นกลุ่ม 1.js
แยกต่างหาก ซึ่งจะโหลดเมื่อคุณกดปุ่มเท่านั้น
![แท็บเครือข่ายเครื่องมือสำหรับนักพัฒนาเว็บหลังจากคลิกปุ่ม แสดงไฟล์ 1.js เพิ่มเติมและรูปภาพที่เพิ่มไว้ที่ด้านล่างของรายการไฟล์](https://web.developers.google.cn/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/devtools-network-tab-the-e42cc1846b47f.png?authuser=1&hl=th)
ในการใช้งานจริง คอมโพเนนต์มักมีขนาดใหญ่กว่ามาก และการโหลดแบบ Lazy Loading จะลดเพย์โหลด JavaScript เริ่มต้นลงได้หลายร้อยกิโลไบต์
การนําเข้าแบบไดนามิกที่มีสัญญาณบอกสถานะการโหลดที่กําหนดเอง
เมื่อโหลดทรัพยากรแบบ Lazy Loading แนวทางปฏิบัติที่ดีคือการระบุตัวบ่งชี้การโหลดในกรณีที่เกิดความล่าช้า ซึ่งใน Next.js คุณทำได้โดยระบุอาร์กิวเมนต์เพิ่มเติมลงในฟังก์ชัน dynamic()
ดังนี้
const Puppy = dynamic(() => import("../components/Puppy"), {
loading: () => <p>Loading...</p>
});
หากต้องการดูการทำงานของตัวตรวจจับการโหลด ให้จำลองการเชื่อมต่อเครือข่ายที่ช้าในเครื่องมือสำหรับนักพัฒนาเว็บ โดยทำดังนี้
หากต้องการดูตัวอย่างเว็บไซต์ ให้กดดูแอป แล้วกดเต็มหน้าจอ
กด "Control+Shift+J" (หรือ "Command+Option+J" ใน Mac) เพื่อเปิดเครื่องมือสำหรับนักพัฒนาเว็บ
คลิกแท็บเครือข่าย
เลือกช่องทำเครื่องหมายปิดใช้แคช
ในรายการแบบเลื่อนลง Throttling ให้เลือก Fast 3G
กดปุ่มคลิกฉัน
ตอนนี้เมื่อคุณคลิกปุ่ม อาจใช้เวลาสักครู่ในการโหลดคอมโพเนนต์และแอป แสดงข้อความ "กำลังโหลด..." ในระหว่างนั้น
![หน้าจอมืดพร้อมข้อความ](https://web.developers.google.cn/static/articles/code-splitting-with-dynamic-imports-in-nextjs/image/a-dark-screen-the-text-914039a8f2589.png?authuser=1&hl=th)
การนำเข้าแบบไดนามิกโดยไม่มี SSR
หากต้องการแสดงผลคอมโพเนนต์เฉพาะในฝั่งไคลเอ็นต์ (เช่น วิดเจ็ตแชท) คุณจะทำได้โดยตั้งค่าตัวเลือก ssr
เป็น false
ดังนี้
const Puppy = dynamic(() => import("../components/Puppy"), {
ssr: false,
});
บทสรุป
การรองรับการนำเข้าแบบไดนามิกทำให้ Next.js สามารถแยกโค้ดระดับคอมโพเนนต์ได้ ซึ่งจะช่วยลดเพย์โหลด JavaScript และปรับปรุงเวลาที่ใช้ในการโหลดแอปพลิเคชันได้ คอมโพเนนต์ทั้งหมดจะแสดงผลฝั่งเซิร์ฟเวอร์โดยค่าเริ่มต้นและคุณปิดใช้ตัวเลือกนี้ได้ทุกเมื่อที่จำเป็น