สำรวจ Next.js Parallel Routes: คู่มือฉบับสมบูรณ์สำหรับการสร้างเลย์เอาต์เพจที่ไดนามิกและยืดหยุ่นด้วยส่วนต่างๆ ที่เป็นอิสระต่อกัน เรียนรู้การนำไปใช้ ประโยชน์ และแนวทางปฏิบัติที่ดีที่สุด
Next.js Parallel Routes: การสร้างเลย์เอาต์เพจแบบไดนามิก
Next.js ซึ่งเป็นเฟรมเวิร์ก React ชั้นนำ มีการพัฒนาอย่างต่อเนื่องเพื่อมอบเครื่องมืออันทรงพลังแก่นักพัฒนาสำหรับการสร้างเว็บแอปพลิเคชันสมัยใหม่ หนึ่งในฟีเจอร์ที่น่าตื่นเต้นที่สุดที่เปิดตัวในเวอร์ชันล่าสุดคือ Parallel Routes ฟีเจอร์นี้ช่วยให้คุณสามารถเรนเดอร์ส่วนต่างๆ ที่เป็นอิสระต่อกันหลายส่วนภายในเลย์เอาต์ของหน้าเดียวกันได้ ซึ่งมอบความยืดหยุ่นและการควบคุมโครงสร้างของแอปพลิเคชันและประสบการณ์ผู้ใช้ได้อย่างที่ไม่เคยมีมาก่อน
Parallel Routes คืออะไร?
โดยปกติแล้ว route ใน Next.js จะสอดคล้องกับ page component เพียงตัวเดียว เมื่อคุณไปยัง route อื่น ทั้งหน้าจะถูกเรนเดอร์ใหม่ Parallel Routes ทำลายกระบวนทัศน์นี้โดยทำให้คุณสามารถเรนเดอร์ component หลายตัว พร้อมกัน ภายในเลย์เอาต์เดียวกัน โดยแต่ละตัวจะถูกจัดการโดย route segment ที่เป็นอิสระของตัวเอง ลองนึกภาพว่ามันคือการแบ่งหน้าของคุณออกเป็นส่วนต่างๆ ที่ชัดเจน โดยแต่ละส่วนมี URL และ lifecycle เป็นของตัวเอง ทั้งหมดอยู่ร่วมกันบนหน้าจอเดียว
สิ่งนี้ปลดล็อกความเป็นไปได้มากมายสำหรับการสร้างส่วนติดต่อผู้ใช้ที่ซับซ้อนและไดนามิกมากขึ้น ตัวอย่างเช่น คุณสามารถใช้ parallel routes เพื่อ:
- แสดงแถบนำทางที่คงที่ควบคู่ไปกับเนื้อหาหลัก
- สร้างหน้าต่างโมดัลหรือแถบด้านข้างโดยไม่ส่งผลกระทบต่อการทำงานหลักของหน้า
- สร้างแดชบอร์ดที่มีวิดเจ็ตอิสระที่สามารถโหลดและอัปเดตแยกกันได้
- ทดสอบ A/B test ของ component เวอร์ชันต่างๆ โดยไม่ส่งผลกระทบต่อโครงสร้างโดยรวมของหน้า
ทำความเข้าใจแนวคิด: Slots
แนวคิดหลักเบื้องหลัง Parallel Routes คือแนวคิดของ "slots" slot คือพื้นที่ที่มีชื่ออยู่ในเลย์เอาต์ของคุณซึ่งเป็นที่สำหรับเรนเดอร์ route segment ที่เจาะจง คุณกำหนด slots เหล่านี้ในไดเรกทอรี app
ของคุณโดยใช้สัญลักษณ์ @
ตามด้วยชื่อ slot ตัวอย่างเช่น @sidebar
แทน slot ที่ชื่อว่า "sidebar"
จากนั้นแต่ละ slot จะสามารถเชื่อมโยงกับ route segment ได้ เมื่อผู้ใช้ไปยัง route ที่เจาะจง Next.js จะเรนเดอร์ component ที่เชื่อมโยงกับ route segment นั้นลงใน slot ที่สอดคล้องกันในเลย์เอาต์
การนำไปใช้งาน: ตัวอย่างที่ปฏิบัติได้จริง
ลองมาดูตัวอย่างการทำงานของ Parallel Routes กัน สมมติว่าคุณกำลังสร้างแอปพลิเคชันอีคอมเมิร์ซ และคุณต้องการแสดงหน้ารายละเอียดสินค้าพร้อมกับแถบด้านข้างของตะกร้าสินค้าที่แสดงผลอยู่ตลอดเวลา
1. โครงสร้างไดเรกทอรี
ขั้นแรก เรามากำหนดโครงสร้างไดเรกทอรีสำหรับแอปพลิเคชันของเรากัน:
app/ product/ [id]/ @cart/ page.js // คอมโพเนนต์ตะกร้าสินค้า page.js // คอมโพเนนต์รายละเอียดสินค้า layout.js // เลย์เอาต์สินค้า layout.js // Root layout
นี่คือคำอธิบายของแต่ละไฟล์:
- app/layout.js: Root layout สำหรับทั้งแอปพลิเคชัน
- app/product/[id]/layout.js: เลย์เอาต์เฉพาะสำหรับหน้ารายละเอียดสินค้า ที่นี่คือที่ที่เราจะกำหนด slots ของเรา
- app/product/[id]/page.js: คอมโพเนนต์หลักของรายละเอียดสินค้า
- app/product/[id]/@cart/page.js: คอมโพเนนต์ตะกร้าสินค้า ซึ่งจะถูกเรนเดอร์ใน
@cart
slot
2. Root Layout (app/layout.js)
โดยทั่วไป Root layout จะมีองค์ประกอบที่ใช้ร่วมกันทั่วทั้งแอปพลิเคชัน เช่น ส่วนหัวและส่วนท้าย
// app/layout.js export default function RootLayout({ children }) { return (My E-commerce App {children} ); }
3. Product Layout (app/product/[id]/layout.js)
นี่คือส่วนสำคัญที่เรากำหนด slots ของเรา เราจะได้รับคอมโพเนนต์สำหรับหน้าสินค้าหลักและตะกร้าสินค้าเป็น props ซึ่งสอดคล้องกับ page.js
และ @cart/page.js
ตามลำดับ
// app/product/[id]/layout.js export default function ProductLayout({ children, cart }) { return (); }{children}
ในตัวอย่างนี้ เราใช้เลย์เอาต์ flexbox แบบง่ายๆ เพื่อวางเนื้อหาสินค้าหลักและแถบด้านข้างของตะกร้าสินค้าไว้ข้างกัน prop children
จะมีผลลัพธ์ที่เรนเดอร์จาก app/product/[id]/page.js
และ prop cart
จะมีผลลัพธ์ที่เรนเดอร์จาก app/product/[id]/@cart/page.js
4. หน้ารายละเอียดสินค้า (app/product/[id]/page.js)
นี่คือหน้า dynamic route มาตรฐานที่แสดงรายละเอียดสินค้าตามพารามิเตอร์ id
// app/product/[id]/page.js export default async function ProductDetails({ params }) { const { id } = params; // ดึงข้อมูลสินค้าตาม ID const product = await fetchProduct(id); return (); } async function fetchProduct(id) { // แทนที่ด้วยโลจิกการดึงข้อมูลจริงของคุณ return new Promise(resolve => setTimeout(() => { resolve({ id, name: `Product ${id}`, description: `Description of Product ${id}`, price: 99.99 }); }, 500)); }Product Details
{product.name}
{product.description}
Price: ${product.price}
5. คอมโพเนนต์ตะกร้าสินค้า (app/product/[id]/@cart/page.js)
คอมโพเนนต์นี้แสดงถึงตะกร้าสินค้า ซึ่งจะถูกเรนเดอร์ใน @cart
slot
// app/product/[id]/@cart/page.js export default function ShoppingCart() { return (); }Shopping Cart
Items in cart: 3
คำอธิบาย
เมื่อผู้ใช้ไปที่ /product/123
Next.js จะ:
- เรนเดอร์ root layout (
app/layout.js
) - เรนเดอร์ product layout (
app/product/[id]/layout.js
) - ภายใน product layout จะเรนเดอร์คอมโพเนนต์รายละเอียดสินค้า (
app/product/[id]/page.js
) ลงใน propchildren
- ในขณะเดียวกัน ก็จะเรนเดอร์คอมโพเนนต์ตะกร้าสินค้า (
app/product/[id]/@cart/page.js
) ลงใน propcart
ผลลัพธ์ที่ได้คือหน้ารายละเอียดสินค้าพร้อมแถบด้านข้างตะกร้าสินค้าที่แสดงผลอยู่ตลอดเวลา ทั้งหมดถูกเรนเดอร์ภายในเลย์เอาต์เดียว
ประโยชน์ของการใช้ Parallel Routes
- ปรับปรุงประสบการณ์ผู้ใช้: สร้างส่วนติดต่อผู้ใช้ที่มีการโต้ตอบและน่าดึงดูดยิ่งขึ้นด้วยองค์ประกอบที่คงที่และส่วนต่างๆ ที่เป็นไดนามิก
- เพิ่มความสามารถในการนำโค้ดกลับมาใช้ใหม่: แบ่งปันคอมโพเนนต์และเลย์เอาต์ระหว่าง routes ต่างๆ ได้ง่ายขึ้น
- ประสิทธิภาพที่เพิ่มขึ้น: โหลดและอัปเดตส่วนต่างๆ ของหน้าได้อย่างอิสระ ลดความจำเป็นในการเรนเดอร์หน้าใหม่ทั้งหมด
- การพัฒนาที่ง่ายขึ้น: จัดการเลย์เอาต์และการโต้ตอบที่ซับซ้อนด้วยโครงสร้างที่เป็นโมดูลและเป็นระเบียบมากขึ้น
- ความสามารถในการทำ A/B Testing: ทดสอบรูปแบบต่างๆ ของส่วนหน้าเฉพาะได้อย่างง่ายดายโดยไม่ส่งผลกระทบต่อทั้งหน้า
ข้อควรพิจารณาและแนวทางปฏิบัติที่ดีที่สุด
- ความขัดแย้งของ Route: ระมัดระวังเพื่อหลีกเลี่ยงความขัดแย้งของ route ระหว่าง parallel routes แต่ละ route segment ควรมีจุดประสงค์ที่ไม่ซ้ำกันและไม่ทับซ้อนกับ segment อื่นๆ
- ความซับซ้อนของเลย์เอาต์: แม้ว่า parallel routes จะให้ความยืดหยุ่น แต่การใช้งานมากเกินไปอาจนำไปสู่เลย์เอาต์ที่ซับซ้อนและดูแลรักษายาก พยายามสร้างสมดุลระหว่างความยืดหยุ่นและความเรียบง่าย
- ผลกระทบต่อ SEO: พิจารณาผลกระทบต่อ SEO ของการใช้ parallel routes โดยเฉพาะอย่างยิ่งหากเนื้อหาใน slots ต่างๆ มีความแตกต่างกันอย่างมีนัยสำคัญ ตรวจสอบให้แน่ใจว่าเครื่องมือค้นหาสามารถ crawl และ index เนื้อหาได้อย่างถูกต้อง ใช้ canonical URLs อย่างเหมาะสม
- การดึงข้อมูล: จัดการการดึงข้อมูลอย่างรอบคอบ โดยเฉพาะเมื่อต้องจัดการกับส่วนต่างๆ ที่เป็นอิสระหลายส่วน พิจารณาใช้ data stores ที่ใช้ร่วมกันหรือกลไกการแคชเพื่อหลีกเลี่ยงการร้องขอข้อมูลที่ซ้ำซ้อน
- การเข้าถึง (Accessibility): ตรวจสอบให้แน่ใจว่าการใช้ parallel route ของคุณสามารถเข้าถึงได้โดยผู้ใช้ทุกคน รวมถึงผู้ที่มีความบกพร่อง ใช้ ARIA attributes และ HTML เชิงความหมายที่เหมาะสมเพื่อมอบประสบการณ์ผู้ใช้ที่ดี
การใช้งานขั้นสูง: Conditional Rendering และ Dynamic Slots
Parallel routes ไม่ได้จำกัดอยู่แค่การกำหนด slot แบบคงที่ คุณยังสามารถใช้ conditional rendering และ dynamic slots เพื่อสร้างเลย์เอาต์ที่ยืดหยุ่นยิ่งขึ้นได้
Conditional Rendering
คุณสามารถเรนเดอร์คอมโพเนนต์ต่างๆ ใน slot ตามเงื่อนไขได้ ขึ้นอยู่กับบทบาทของผู้ใช้ สถานะการยืนยันตัวตน หรือปัจจัยอื่นๆ
// app/product/[id]/layout.js import { getUserRole } from '../../utils/auth'; export default async function ProductLayout({ children, cart }) { const userRole = await getUserRole(); return (); } function AdminPanel() { return ({children} ); }Admin Panel
Manage product details here.
ในตัวอย่างนี้ หากผู้ใช้มีบทบาทเป็น 'admin' คอมโพเนนต์ AdminPanel
จะถูกเรนเดอร์ใน @cart
slot แทนที่จะเป็นตะกร้าสินค้า
Dynamic Slots
แม้ว่าจะไม่ค่อยพบบ่อยนัก แต่ตามทฤษฎีแล้วคุณ *สามารถ* สร้างชื่อ slot แบบไดนามิกได้ แต่โดยทั่วไปไม่แนะนำเนื่องจากความซับซ้อนและผลกระทบที่อาจเกิดขึ้นกับประสิทธิภาพ การยึดตาม slots ที่กำหนดไว้ล่วงหน้าและเข้าใจได้ง่ายจะดีกว่า หากมีความจำเป็นต้องใช้ "slots" แบบไดนามิก ให้พิจารณาแนวทางแก้ไขอื่น เช่น การใช้คอมโพเนนต์ React มาตรฐานพร้อม props และ conditional rendering
ตัวอย่างการใช้งานจริงและกรณีศึกษา
ลองมาสำรวจตัวอย่างการใช้งาน parallel routes ในแอปพลิเคชันประเภทต่างๆ กัน:
- แพลตฟอร์มอีคอมเมิร์ซ: แสดงตะกร้าสินค้า คำแนะนำสินค้า หรือข้อมูลบัญชีผู้ใช้ควบคู่ไปกับหน้ารายละเอียดสินค้าหรือหน้าหมวดหมู่
- แดชบอร์ด: สร้างแดชบอร์ดที่มีวิดเจ็ตอิสระสำหรับแสดงเมตริก แผนภูมิ และรายงาน โดยแต่ละวิดเจ็ตสามารถโหลดและอัปเดตแยกกันได้โดยไม่ส่งผลกระทบต่อทั้งแดชบอร์ด แดชบอร์ดการขายอาจแสดงข้อมูลทางภูมิศาสตร์ใน parallel route หนึ่ง และประสิทธิภาพของสินค้าในอีก route หนึ่ง ทำให้ผู้ใช้สามารถปรับแต่งสิ่งที่เห็นได้โดยไม่ต้องโหลดหน้าใหม่ทั้งหมด
- แอปพลิเคชันโซเชียลมีเดีย: แสดงแถบด้านข้างสำหรับแชทหรือแผงการแจ้งเตือนควบคู่ไปกับฟีดหลักหรือหน้าโปรไฟล์
- ระบบจัดการเนื้อหา (CMS): แสดงหน้าต่างแสดงตัวอย่างหรือเครื่องมือแก้ไขควบคู่ไปกับเนื้อหาที่กำลังแก้ไข parallel route สามารถแสดงตัวอย่างสดของบทความที่กำลังเขียน ซึ่งอัปเดตแบบเรียลไทม์เมื่อมีการเปลี่ยนแปลง
- แพลตฟอร์มการเรียนรู้: แสดงเนื้อหาหลักสูตรควบคู่ไปกับการติดตามความคืบหน้าหรือฟีเจอร์การปฏิสัมพันธ์ทางสังคม
- แอปพลิเคชันทางการเงิน: แสดงราคาหุ้นแบบเรียลไทม์หรือสรุปพอร์ตโฟลิโอควบคู่ไปกับข่าวสารหรือบทวิเคราะห์ ลองนึกภาพเว็บไซต์ข่าวการเงินที่ใช้ parallel routes เพื่อแสดงข้อมูลตลาดสดควบคู่ไปกับข่าวด่วน ทำให้ผู้ใช้เห็นภาพรวมของภูมิทัศน์ทางการเงินได้อย่างครอบคลุม
- เครื่องมือการทำงานร่วมกันระดับโลก: ช่วยให้สามารถแก้ไขเอกสารหรือโค้ดพร้อมกันได้โดยมีแผงการประชุมทางวิดีโอหรือแชทแสดงผลอยู่ตลอดเวลา ทีมวิศวกรที่กระจายตัวอยู่ในซานฟรานซิสโก ลอนดอน และโตเกียว สามารถใช้ parallel routes เพื่อทำงานบนเอกสารการออกแบบเดียวกันแบบเรียลไทม์ โดยมีวิดีโอคอลแสดงผลอย่างถาวรในแถบด้านข้าง ส่งเสริมการทำงานร่วมกันอย่างราบรื่นข้ามเขตเวลา
สรุป
Next.js Parallel Routes เป็นฟีเจอร์ที่ทรงพลังซึ่งเปิดโลกแห่งความเป็นไปได้ใหม่ๆ สำหรับการสร้างเว็บแอปพลิเคชันที่ไดนามิกและยืดหยุ่น ด้วยการช่วยให้คุณสามารถเรนเดอร์ส่วนต่างๆ ที่เป็นอิสระหลายส่วนภายในเลย์เอาต์ของหน้าเดียวกันได้ parallel routes ทำให้คุณสามารถสร้างประสบการณ์ผู้ใช้ที่น่าดึงดูดยิ่งขึ้น เพิ่มความสามารถในการนำโค้ดกลับมาใช้ใหม่ และทำให้กระบวนการพัฒนาง่ายขึ้น แม้ว่าการพิจารณาความซับซ้อนที่อาจเกิดขึ้นและปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดจะเป็นสิ่งสำคัญ แต่การเชี่ยวชาญ parallel routes สามารถเพิ่มพูนทักษะการพัฒนา Next.js ของคุณได้อย่างมีนัยสำคัญ และช่วยให้คุณสร้างเว็บแอปพลิเคชันที่เป็นนวัตกรรมได้อย่างแท้จริง
ในขณะที่ Next.js ยังคงพัฒนาต่อไป Parallel Routes จะกลายเป็นเครื่องมือที่สำคัญมากขึ้นสำหรับนักพัฒนาที่ต้องการผลักดันขอบเขตของสิ่งที่เป็นไปได้บนเว็บอย่างไม่ต้องสงสัย ลองทดลองกับแนวคิดที่ได้อธิบายไว้ในคู่มือนี้ และค้นพบว่า Parallel Routes สามารถเปลี่ยนแปลงแนวทางการสร้างเว็บแอปพลิเคชันสมัยใหม่ของคุณได้อย่างไร