ไทย

ปลดล็อก UI ที่ยืดหยุ่นและไดนามิกใน Next.js คู่มือฉบับสมบูรณ์ของเราครอบคลุม Route Groups สำหรับการจัดระเบียบและ Parallel Routes สำหรับแดชบอร์ดที่ซับซ้อน ยกระดับทักษะของคุณทันที!

การเรียนรู้ Next.js App Router อย่างเชี่ยวชาญ: เจาะลึกสถาปัตยกรรม Route Groups และ Parallel Routes

การเปิดตัว Next.js App Router ถือเป็นการเปลี่ยนแปลงกระบวนทัศน์ครั้งสำคัญในการสร้างเว็บแอปพลิเคชันด้วยเฟรมเวิร์ก React ยอดนิยม App Router ได้เปลี่ยนจากรูปแบบเดิมที่อิงตามไฟล์ของ Pages Router ไปสู่โมเดลที่ทรงพลัง ยืดหยุ่น และเน้นการทำงานบนเซิร์ฟเวอร์มากขึ้น วิวัฒนาการนี้ช่วยให้เราสามารถสร้างส่วนต่อประสานกับผู้ใช้ (UI) ที่ซับซ้อนและมีประสิทธิภาพสูง พร้อมการควบคุมและการจัดระเบียบที่ดียิ่งขึ้น หนึ่งในฟีเจอร์ที่เปลี่ยนแปลงมากที่สุดที่เปิดตัวคือ Route Groups และ Parallel Routes

สำหรับนักพัฒนาที่ต้องการสร้างแอปพลิเคชันระดับองค์กร การทำความเข้าใจแนวคิดทั้งสองนี้อย่างถ่องแท้ไม่ใช่แค่เรื่องที่เป็นประโยชน์—แต่เป็นสิ่งจำเป็นอย่างยิ่ง สิ่งเหล่านี้ช่วยแก้ปัญหาความท้าทายทางสถาปัตยกรรมที่พบบ่อยเกี่ยวกับการจัดการเลย์เอาต์ การจัดระเบียบเส้นทาง (route) และการสร้างอินเทอร์เฟซแบบหลายแผง (multi-panel) ที่ไดนามิก เช่น แดชบอร์ด คู่มือนี้จะพาไปสำรวจ Route Groups และ Parallel Routes อย่างครอบคลุม ตั้งแต่แนวคิดพื้นฐานไปจนถึงกลยุทธ์การนำไปใช้ขั้นสูงและแนวปฏิบัติที่ดีที่สุดสำหรับนักพัฒนาทั่วโลก

ทำความเข้าใจ Next.js App Router: ทบทวนอย่างรวดเร็ว

ก่อนที่เราจะเจาะลึกรายละเอียด เรามาทบทวนหลักการหลักของ App Router กันสั้นๆ สถาปัตยกรรมของมันสร้างขึ้นบนระบบที่ใช้ไดเรกทอรีเป็นหลัก โดยที่โฟลเดอร์จะกำหนดส่วนของ URL (URL segments) และไฟล์พิเศษภายในโฟลเดอร์เหล่านี้จะกำหนด UI และพฤติกรรมสำหรับส่วนนั้นๆ:

โครงสร้างนี้ เมื่อรวมกับการใช้ React Server Components (RSCs) เป็นค่าเริ่มต้น จะส่งเสริมแนวทางการทำงานที่เน้นเซิร์ฟเวอร์เป็นอันดับแรก ซึ่งสามารถปรับปรุงประสิทธิภาพและรูปแบบการดึงข้อมูลได้อย่างมาก Route Groups และ Parallel Routes เป็นรูปแบบการใช้งานขั้นสูงที่ต่อยอดจากรากฐานนี้

ไขข้อข้องใจ Route Groups: จัดระเบียบโปรเจกต์ของคุณเพื่อความเรียบร้อยและรองรับการขยายตัว

เมื่อแอปพลิเคชันเติบโตขึ้น จำนวนของ route อาจกลายเป็นเรื่องที่จัดการได้ยาก คุณอาจมีกลุ่มของหน้าเว็บสำหรับการตลาด, อีกกลุ่มสำหรับการยืนยันตัวตนผู้ใช้, และกลุ่มที่สามสำหรับแดชบอร์ดหลักของแอปพลิเคชัน ในทางตรรกะแล้ว สิ่งเหล่านี้เป็นส่วนที่แยกจากกัน แต่คุณจะจัดระเบียบมันในระบบไฟล์ของคุณอย่างไรโดยไม่ทำให้ URL ของคุณรก? นี่คือปัญหาที่ Route Groups เข้ามาแก้ไขได้อย่างแม่นยำ

Route Groups คืออะไร?

Route Group คือกลไกในการจัดระเบียบไฟล์และส่วนของ route ของคุณให้เป็นกลุ่มตามตรรกะ โดยไม่ส่งผลกระทบต่อโครงสร้าง URL คุณสามารถสร้าง Route Group ได้โดยการใส่ชื่อโฟลเดอร์ไว้ในวงเล็บ เช่น (marketing) หรือ (app)

ชื่อโฟลเดอร์ที่อยู่ในวงเล็บมีไว้เพื่อการจัดระเบียบเท่านั้น Next.js จะไม่นำมาพิจารณาเลยเมื่อกำหนดเส้นทาง URL ตัวอย่างเช่น ไฟล์ที่อยู่ที่ app/(marketing)/about/page.js จะถูกแสดงผลที่ URL /about ไม่ใช่ /(marketing)/about

กรณีการใช้งานหลักและประโยชน์ของ Route Groups

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

1. การสร้างเลย์เอาต์ที่แตกต่างกันสำหรับส่วนของ Route

นี่เป็นกรณีการใช้งานที่พบบ่อยและทรงพลังที่สุด ลองจินตนาการถึงเว็บแอปพลิเคชันที่มีสองส่วนหลัก:

หากไม่มี Route Groups การใช้ root layouts ที่แตกต่างกันกับส่วนเหล่านี้จะซับซ้อน แต่ด้วย Route Groups มันกลับง่ายอย่างไม่น่าเชื่อ คุณสามารถสร้างไฟล์ layout.js ที่เป็นเอกลักษณ์ภายในแต่ละกลุ่มได้

นี่คือโครงสร้างไฟล์ทั่วไปสำหรับสถานการณ์นี้:

app/
├── (marketing)/
│   ├── layout.js      // เลย์เอาต์สาธารณะพร้อมส่วนหัว/ส่วนท้ายสำหรับการตลาด
│   ├── page.js        // แสดงผลที่ '/'
│   └── about/
│       └── page.js    // แสดงผลที่ '/about'
├── (app)/
│   ├── layout.js      // เลย์เอาต์แดชบอร์ดพร้อมแถบด้านข้าง
│   ├── dashboard/
│   │   └── page.js    // แสดงผลที่ '/dashboard'
│   └── settings/
│       └── page.js    // แสดงผลที่ '/settings'
└── layout.js          // Root layout (เช่น สำหรับแท็ก <html> และ <body>)

ในสถาปัตยกรรมนี้:

2. การเลือกให้ส่วนใดส่วนหนึ่งไม่อยู่ในเลย์เอาต์ที่ใช้ร่วมกัน

บางครั้ง หน้าหรือส่วนใดส่วนหนึ่งจำเป็นต้องแยกตัวออกจาก parent layout โดยสิ้นเชิง ตัวอย่างทั่วไปคือกระบวนการชำระเงินหรือหน้า landing page พิเศษที่ไม่ควรมีการนำทางหลักของเว็บไซต์ คุณสามารถทำได้โดยการวาง route นั้นไว้ในกลุ่มที่ไม่ได้ใช้เลย์เอาต์ระดับบนร่วมกัน แม้จะฟังดูซับซ้อน แต่ก็หมายถึงการให้ route group นั้นมี layout.js ระดับบนสุดของตัวเองซึ่งไม่ได้ render `children` จาก root layout

ตัวอย่างการปฏิบัติ: การสร้างแอปพลิเคชันที่มีหลายเลย์เอาต์

เรามาสร้างเวอร์ชันย่อของโครงสร้าง marketing/app ที่อธิบายไว้ข้างต้นกัน

1. Root Layout (app/layout.js)

เลย์เอาต์นี้เรียบง่ายและนำไปใช้กับทุกๆ หน้า มันกำหนดโครงสร้าง HTML ที่จำเป็น

// app/layout.js
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

2. เลย์เอาต์สำหรับการตลาด (app/(marketing)/layout.js)

เลย์เอาต์นี้มีส่วนหัวและส่วนท้ายสำหรับหน้าสาธารณะ

// app/(marketing)/layout.js
export default function MarketingLayout({ children }) {
  return (
    <div>
      <header>Marketing Header</header>
      <main>{children}</main>
      <footer>Marketing Footer</footer>
    </div>
  );
}

3. เลย์เอาต์แดชบอร์ดของแอป (app/(app)/layout.js)

เลย์เอาต์นี้มีโครงสร้างที่แตกต่างออกไป โดยมีแถบด้านข้างสำหรับผู้ใช้ที่ยืนยันตัวตนแล้ว

// app/(app)/layout.js
export default function AppLayout({ children }) {
  return (
    <div style={{ display: 'flex' }}>
      <aside style={{ width: '200px', borderRight: '1px solid #ccc' }}>
        Dashboard Sidebar
      </aside>
      <main style={{ flex: 1, padding: '20px' }}>{children}</main>
    </div>
  );
}

ด้วยโครงสร้างนี้ การเข้าไปที่ /about จะแสดงหน้าเว็บพร้อมกับ `MarketingLayout` ในขณะที่การเข้าไปที่ /dashboard จะแสดงผลพร้อมกับ `AppLayout` โดยที่ URL ยังคงสะอาดและมีความหมายตามหลัก semantic ในขณะที่โครงสร้างไฟล์ของโปรเจกต์ของเราก็ถูกจัดระเบียบอย่างสมบูรณ์แบบและพร้อมสำหรับการขยายตัว

ปลดล็อก UI แบบไดนามิกด้วย Parallel Routes

ในขณะที่ Route Groups ช่วยจัดระเบียบส่วนต่างๆ ของแอปพลิเคชัน Parallel Routes จะจัดการกับความท้าทายที่แตกต่างออกไป: การแสดงมุมมองของหน้าเว็บหลายๆ ส่วนที่เป็นอิสระต่อกันภายในเลย์เอาต์เดียว นี่เป็นข้อกำหนดทั่วไปสำหรับแดชบอร์ดที่ซับซ้อน, ฟีดโซเชียลมีเดีย, หรือ UI ใดๆ ที่ต้องการให้แผงต่างๆ สามารถแสดงผลและจัดการได้พร้อมกัน

Parallel Routes คืออะไร?

Parallel Routes ช่วยให้คุณสามารถ render หน้าเว็บหนึ่งหน้าหรือมากกว่าพร้อมกันภายในเลย์เอาต์เดียวกันได้ routes เหล่านี้ถูกกำหนดโดยใช้รูปแบบโฟลเดอร์พิเศษที่เรียกว่า slots (สล็อต) สล็อตถูกสร้างขึ้นโดยใช้ไวยากรณ์ @folderName พวกมันไม่ได้เป็นส่วนหนึ่งของโครงสร้าง URL แต่จะถูกส่งเป็น props ไปยังไฟล์ `layout.js` ที่เป็น parent ร่วมที่ใกล้ที่สุดโดยอัตโนมัติ

ตัวอย่างเช่น หากคุณมีเลย์เอาต์ที่ต้องแสดงฟีดกิจกรรมของทีมและแผนภูมิการวิเคราะห์ข้างๆ กัน คุณสามารถกำหนดสล็อตสองช่องได้: `@team` และ `@analytics`

แนวคิดหลัก: Slots

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

พิจารณาคอมโพเนนต์เลย์เอาต์นี้:

// เลย์เอาต์ที่ยอมรับสองสล็อต: 'team' และ 'analytics'
export default function DashboardLayout({ children, team, analytics }) {
  return (
    <div>
      {children}
      <div style={{ display: 'flex' }}>
        {team}
        {analytics}
      </div>
    </div>
  );
}

ในที่นี้ `children`, `team`, และ `analytics` ล้วนเป็นสล็อต `children` เป็นสล็อตโดยนัย (implicit) ที่สอดคล้องกับไฟล์ page.js มาตรฐานในไดเรกทอรี ส่วน `team` และ `analytics` เป็นสล็อตโดยชัดแจ้ง (explicit) ที่ต้องสร้างขึ้นด้วยคำนำหน้า `@` ในระบบไฟล์

คุณสมบัติหลักและข้อดี

สถานการณ์จริง: การสร้างแดชบอร์ดที่ซับซ้อน

เรามาออกแบบแดชบอร์ดที่ URL /dashboard กัน มันจะมีพื้นที่เนื้อหาหลัก, แผงกิจกรรมของทีม, และแผงวิเคราะห์ประสิทธิภาพ

โครงสร้างไฟล์:

app/
└── dashboard/
    ├── @analytics/
    │   ├── page.js          // UI สำหรับสล็อต analytics
    │   └── loading.js     // UI การโหลดเฉพาะสำหรับ analytics
    ├── @team/
    │   └── page.js          // UI สำหรับสล็อต team
    ├── layout.js            // เลย์เอาต์ที่ควบคุมการจัดวางสล็อต
    └── page.js              // สล็อต 'children' โดยนัย (เนื้อหาหลัก)

1. เลย์เอาต์แดชบอร์ด (app/dashboard/layout.js)

เลย์เอาต์นี้รับและจัดเรียงสล็อตทั้งสาม

// app/dashboard/layout.js
export default function DashboardLayout({ children, analytics, team }) {
  const isLoggedIn = true; // แทนที่ด้วยตรรกะการยืนยันตัวตนจริง

  return isLoggedIn ? (
    <div>
      <h1>Main Dashboard</h1>
      {children}
      <div style={{ marginTop: '20px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px' }}>
        <div style={{ border: '1px solid blue', padding: '10px' }}>
          <h2>Team Activity</h2>
          {team}
        </div>
        <div style={{ border: '1px solid green', padding: '10px' }}>
          <h2>Performance Analytics</h2>
          {analytics}
        </div>
      </div>
    </div>
  ) : (
    <div>Please log in to view the dashboard.</div>
  );
}

2. หน้าของสล็อต (เช่น app/dashboard/@analytics/page.js)

ไฟล์ page.js ของแต่ละสล็อตจะมี UI สำหรับแผงนั้นๆ

// app/dashboard/@analytics/page.js
async function getAnalyticsData() {
  // จำลองการร้องขอข้อมูลผ่านเครือข่าย
  await new Promise(resolve => setTimeout(resolve, 3000));
  return { views: '1.2M', revenue: '$50,000' };
}

export default async function AnalyticsPage() {
  const data = await getAnalyticsData();
  return (
    <div>
      <p>Page Views: {data.views}</p>
      <p>Revenue: {data.revenue}</p>
    </div>
  );
}

// app/dashboard/@analytics/loading.js
export default function Loading() {
  return <p>Loading analytics data...</p>;
}

ด้วยการตั้งค่านี้ เมื่อผู้ใช้ไปที่ /dashboard, Next.js จะ render `DashboardLayout` เลย์เอาต์จะได้รับเนื้อหาที่ render แล้วจาก dashboard/page.js, dashboard/@team/page.js, และ dashboard/@analytics/page.js เป็น props และจัดวางตามตำแหน่งที่กำหนดไว้ สิ่งสำคัญคือแผงการวิเคราะห์จะแสดงสถานะ loading.js ของตัวเองเป็นเวลา 3 วินาทีโดยไม่ขัดขวางการ render ส่วนที่เหลือของแดชบอร์ด

การจัดการ Route ที่ไม่ตรงกันด้วย `default.js`

คำถามสำคัญเกิดขึ้น: จะเกิดอะไรขึ้นถ้า Next.js ไม่สามารถดึงสถานะที่ใช้งานอยู่ของสล็อตสำหรับ URL ปัจจุบันได้? ตัวอย่างเช่น ในระหว่างการโหลดครั้งแรกหรือการรีโหลดหน้าเว็บ URL อาจเป็น /dashboard ซึ่งไม่ได้ให้คำแนะนำที่เจาะจงว่าจะแสดงอะไรภายในสล็อต @team หรือ @analytics โดยปกติแล้ว Next.js จะ render ข้อผิดพลาด 404

เพื่อป้องกันปัญหานี้ เราสามารถให้ UI สำรองได้โดยการสร้างไฟล์ default.js ภายใน parallel route

ตัวอย่าง:

// app/dashboard/@analytics/default.js
export default function DefaultAnalyticsPage() {
  return (
    <div>
      <p>No analytics data selected.</p>
    </div>
  );
}

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

การรวม Route Groups และ Parallel Routes สำหรับสถาปัตยกรรมขั้นสูง

พลังที่แท้จริงของ App Router จะปรากฏเมื่อคุณรวมฟีเจอร์ต่างๆ เข้าด้วยกัน Route Groups และ Parallel Routes ทำงานร่วมกันได้อย่างสวยงามเพื่อสร้างสถาปัตยกรรมแอปพลิเคชันที่ซับซ้อนและมีการจัดระเบียบอย่างดี

กรณีการใช้งาน: ตัวแสดงเนื้อหาหลายรูปแบบ (Multi-Modal Content Viewer)

ลองจินตนาการถึงแพลตฟอร์มอย่างแกลเลอรีสื่อหรือโปรแกรมดูเอกสารที่ผู้ใช้สามารถดูรายการได้ แต่ยังสามารถเปิดหน้าต่างโมดัล (modal) เพื่อดูรายละเอียดโดยไม่สูญเสียบริบทของหน้าที่อยู่เบื้องหลังได้ สิ่งนี้มักถูกเรียกว่า "Intercepting Route" และเป็นรูปแบบที่มีประสิทธิภาพซึ่งสร้างขึ้นบน parallel routes

เรามาสร้างแกลเลอรีรูปภาพกัน เมื่อคุณคลิกที่รูปภาพ มันจะเปิดขึ้นในโมดัล แต่ถ้าคุณรีเฟรชหน้าหรือไปที่ URL ของรูปภาพโดยตรง มันควรจะแสดงหน้าเฉพาะสำหรับรูปภาพนั้น

โครงสร้างไฟล์:

app/
├── @modal/(..)(..)photos/[id]/page.js  // route ที่ถูกดักจับสำหรับโมดัล
├── photos/
│   └── [id]/
│       └── page.js                  // หน้าแสดงรูปภาพโดยเฉพาะ
├── layout.js                        // Root layout ที่รับสล็อต @modal
└── page.js                          // หน้าแกลเลอรีหลัก

คำอธิบาย:

รูปแบบนี้เป็นการรวม parallel routes (สล็อต @modal) เข้ากับรูปแบบการกำหนดเส้นทางขั้นสูงเพื่อสร้างประสบการณ์ผู้ใช้ที่ราบรื่นซึ่งหากจะทำเองนั้นซับซ้อนมาก

แนวปฏิบัติที่ดีที่สุดและข้อผิดพลาดที่พบบ่อย

แนวปฏิบัติที่ดีที่สุดสำหรับ Route Groups

แนวปฏิบัติที่ดีที่สุดสำหรับ Parallel Routes

ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง

บทสรุป: สร้างอนาคตของเว็บแอปพลิเคชัน

Next.js App Router พร้อมด้วยฟีเจอร์อย่าง Route Groups และ Parallel Routes มอบรากฐานที่แข็งแกร่งและสามารถขยายขนาดได้สำหรับการพัฒนาเว็บสมัยใหม่ Route Groups นำเสนอวิธีแก้ปัญหาที่สง่างามสำหรับการจัดระเบียบโค้ดและการใช้เลย์เอาต์ที่แตกต่างกันโดยไม่กระทบต่อความหมายของ URL ส่วน Parallel Routes ปลดล็อกความสามารถในการสร้างอินเทอร์เฟซแบบหลายแผงที่ไดนามิกพร้อมสถานะที่เป็นอิสระต่อกัน ซึ่งก่อนหน้านี้ทำได้เฉพาะผ่านการจัดการสถานะฝั่งไคลเอ็นต์ที่ซับซ้อนเท่านั้น

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

การเรียนรู้ Next.js App Router อย่างเชี่ยวชาญ: เจาะลึกสถาปัตยกรรม Route Groups และ Parallel Routes | MLOG