การกำหนดเส้นทางฝั่งไคลเอ็นต์ใน Next.js

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

ไฮเปอร์ลิงก์เป็นอัญมณีล้ำค่าของเว็บตั้งแต่เริ่มก่อตั้ง ตาม MDN ไฮเปอร์ลิงก์เป็นสิ่งที่ทำให้เว็บ เป็นเว็บ แม้ว่าจะใช้เพื่อวัตถุประสงค์ต่างๆ เช่น การเชื่อมโยงระหว่างเอกสาร การใช้งานหลักคือการอ้างอิงหน้าเว็บต่างๆ ที่ระบุได้ด้วยที่อยู่เว็บหรือ URL ที่ไม่ซ้ำกัน

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

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

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

การกำหนดเส้นทางและการแสดงผล

การกำหนดเส้นทางและการแสดงผลเป็นส่วนเสริมซึ่งกันและกัน และจะมีบทบาทสำคัญในบทความนี้ ฉันชอบวิธีที่ Gaurav อธิบายพวกเขา:

การกำหนดเส้นทาง เป็นกระบวนการที่ผู้ใช้ถูกนำทางไปยังหน้าต่างๆ บนเว็บไซต์

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

ใช้เวลาห้านาทีเพื่อคิดเรื่องนี้

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

ขึ้นอยู่กับข้อกำหนดในการดึงข้อมูลของคุณ คุณอาจพบว่าตัวเองใช้ฟังก์ชันดึงข้อมูลในตัว เช่น getStaticProps , getStaticPaths หรือ getServerSideProps เครื่องมือดึงข้อมูลฝั่งไคลเอ็นต์ เช่น SWR, react-query หรือวิธีการดึงข้อมูลแบบเดิม เช่น fetch-on- แสดงผล, ดึงข้อมูลแล้วแสดงผล, แสดงผลตามที่คุณดึงข้อมูล (ด้วยความใจจดใจจ่อ)

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

ให้เริ่มต้นด้วยส่วนสำคัญพื้นฐาน: Next.js มีเราเตอร์ที่ใช้ระบบไฟล์ที่สร้างขึ้นจากแนวคิดของเพจ

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

หน้า

หน้าใน Next.js คือ React Components ที่พร้อมใช้งานเป็นเส้นทางโดยอัตโนมัติ ไฟล์เหล่านี้จะถูกส่งออกเป็นการส่งออกเริ่มต้นจากไดเร็กทอรีเพจที่มีนามสกุลไฟล์ที่รองรับ เช่น . .js , .jsx , .ts หรือ . .tsx

แอป Next.js ทั่วไปจะมีโครงสร้างโฟลเดอร์ที่มีไดเร็กทอรีระดับบนสุด เช่น เพจ สาธารณะ และ สไตล์

 next-app ├── node_modules ├── pages │ ├── index.js // path: base-url (/) │ ├── books.jsx // path: /books │ └── book.ts // path: /book ├── public ├── styles ├── .gitignore ├── package.json └── README.md

แต่ละหน้าเป็นองค์ประกอบ React:

 // pages/books.js — `base-url/book` export default function Book() { return

หนังสือ

}

หมายเหตุ : โปรดทราบว่าเพจสามารถเรียกว่า “ตัวจัดการเส้นทาง” ได้เช่นกัน

หน้ากำหนดเอง

เหล่านี้เป็นหน้าพิเศษที่อยู่ในไดเร็กทอรี เพจ แต่ไม่ได้เข้าร่วมในการกำหนดเส้นทาง โดยนำหน้าด้วยสัญลักษณ์ขีดล่าง เช่น _app.js และ _document.js

  • _app.js
    นี่คือองค์ประกอบแบบกำหนดเองที่อยู่ในโฟลเดอร์เพจ Next.js ใช้ส่วนประกอบนี้เพื่อเริ่มต้นหน้า
  • _document.js
    เช่นเดียวกับ _app.js _document.js เป็นองค์ประกอบที่กำหนดเองที่ Next.js ใช้เพื่อเสริมแท็กแอปพลิเคชัน <html> และ <body> นี่เป็นสิ่งจำเป็นเนื่องจากหน้า Next.js ข้ามคำจำกัดความของมาร์กอัปของเอกสารโดยรอบ
 next-app ├── node_modules ├── pages │ ├── _app.js // ️ Custom page (unavailable as a route) │ ├── _document.jsx // ️ Custom page (unavailable as a route) │ └── index.ts // path: base-url (/) ├── public ├── styles ├── .gitignore ├── package.json └── README.md

การเชื่อมโยงระหว่างหน้า

Next.js เปิดเผยองค์ประกอบ Link จาก API next/link ที่สามารถใช้ดำเนินการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์ระหว่างหน้าได้

 // Import the <Link/> component import Link from "next/link"; // This could be a page component export default function TopNav() { return ( <nav> <Link href="/">Home</Link> <Link href="/">Publications</Link> <Link href="/">About</Link> </nav> ) } // This could be a non-page component export default function Publications() { return ( <section> <TopNav/> {/* ... */} </section> ) }

ส่วนประกอบ Link สามารถใช้ได้ภายในส่วนประกอบใดๆ หน้าหรือไม่ก็ได้ เมื่อใช้ในรูปแบบพื้นฐานที่สุดตามตัวอย่างข้างต้น คอมโพเนนต์ Link จะแปลเป็นไฮเปอร์ลิงก์ที่มีแอตทริบิวต์ href (เพิ่มเติมเกี่ยวกับ Link ในหัวข้อถัดไป/ส่วนลิงค์ด้านล่าง.)

การกำหนดเส้นทาง

ระบบกำหนดเส้นทางตามไฟล์ Next.js สามารถใช้กำหนดรูปแบบเส้นทางที่พบบ่อยที่สุดได้ เพื่อรองรับรูปแบบเหล่านี้ แต่ละเส้นทางจะแยกตาม คำจำกัดความ

เส้นทางดัชนี

โดยค่าเริ่มต้น ในแอป Next.js เส้นทางเริ่มต้น/เริ่มต้นคือ pages/index.js ซึ่งทำหน้าที่เป็นจุดเริ่มต้นของแอปพลิเคชันของคุณโดยอัตโนมัติเป็น / ด้วย URL พื้นฐานของ localhost:3000 เส้นทางดัชนีนี้สามารถเข้าถึงได้ที่ระดับ URL พื้นฐานของแอปพลิเคชันในเบราว์เซอร์

เส้นทางดัชนีจะทำหน้าที่เป็นเส้นทางเริ่มต้นสำหรับแต่ละไดเรกทอรีโดยอัตโนมัติ และสามารถขจัดความซ้ำซ้อนในการตั้งชื่อได้ โครงสร้างไดเร็กทอรีด้านล่างแสดงเส้นทางสองเส้นทาง: / และ /home

 next-app └── pages ├── index.js // path: base-url (/) └── home.js // path: /home

การกำจัดมีความชัดเจนมากขึ้นด้วย เส้นทางที่ซ้อนกัน

เส้นทางที่ซ้อนกัน

เส้นทางเช่น pages/book มีความลึกหนึ่งระดับ หากต้องการให้ลึกยิ่งขึ้นคือการสร้างเส้นทางที่ซ้อนกัน ซึ่งต้องมีโครงสร้างโฟลเดอร์ที่ซ้อนกัน ด้วย URL ฐานของ https://www.smashingmagazine.com คุณสามารถเข้าถึงเส้นทาง https://www.smashingmagazine.com/printed-books/printed-books โดยสร้างโครงสร้างโฟลเดอร์คล้ายกับด้านล่าง:

 next-app └── pages ├── index.js // top index route └── printed-books // nested route └── printed-books.js // path: /printed-books/printed-books

หรือขจัดความซ้ำซ้อนของเส้นทางด้วยเส้นทางดัชนีและเข้าถึงเส้นทางสำหรับหนังสือที่พิมพ์ได้ที่ https://www.smashingmagazine.com/printed-books

 next-app └── pages ├── index.js // top index route └── printed-books // nested route └── index.js // path: /printed-books

เส้นทางแบบไดนามิก ยังมีบทบาทสำคัญในการกำจัดความซ้ำซ้อน

เส้นทางแบบไดนามิก

จากตัวอย่างก่อนหน้านี้ เราใช้เส้นทางดัชนีเพื่อเข้าถึงหนังสือที่พิมพ์ทั้งหมด ในการเข้าถึงหนังสือแต่ละเล่มต้องสร้างเส้นทางที่แตกต่างกันสำหรับหนังสือแต่ละเล่มเช่น:

 // ️ Don't do this. next-app └── pages ├── index.js // top index route └── printed-books // nested route ├── index.js // path: /printed-books ├── typesript-in-50-lessons.js // path: /printed-books/typesript-in-50-lessons ├── checklist-cards.js // path: /printed-books/checklist-cards ├── ethical-design-handbook.js // path: /printed-books/ethical-design-handbook ├── inclusive-components.js // path: /printed-books/inclusive-components └── click.js // path: /printed-books/click

ซึ่งมีความซ้ำซ้อนสูง ไม่สามารถปรับขนาดได้ และสามารถแก้ไขได้ด้วยเส้นทางแบบไดนามิก เช่น:

 // Do this instead. next-app └── pages ├── index.js // top index route └── printed-books ├── index.js // path: /printed-books └── [book-id].js // path: /printed-books/:book-id

ไวยากรณ์วงเล็บ — [book-id] — คือ เซ็กเมนต์ไดนามิก และไม่จำกัดเฉพาะไฟล์เท่านั้น นอกจากนี้ยังสามารถใช้กับโฟลเดอร์ดังตัวอย่างด้านล่าง ทำให้ผู้เขียนสามารถดูได้ที่เส้นทาง /printed-books/:book-id/author

 next-app └── pages ├── index.js // top index route └── printed-books ├── index.js // path: /printed-books └── [book-id] └── author.js // path: /printed-books/:book-id/author

เซ็กเมนต์ไดนามิกของเส้นทางถูกเปิดเผยเป็นพารามิเตอร์การสืบค้นที่สามารถเข้าถึงได้ในองค์ประกอบการเชื่อมต่อใด ๆ ที่เกี่ยวข้องกับเส้นทางด้วยอ็อบเจ็กต์ query ของ useRouter() hook — (เพิ่มเติมเกี่ยวกับสิ่งนี้ในส่วน API ถัดไป/เราเตอร์ ).

 // printed-books/:book-id import { useRouter } from 'next/router'; export default function Book() { const { query } = useRouter(); return ( <div> <h1> book-id <em>{query['book-id']}</em> </h1> </div> ); }
 // /printed-books/:book-id/author import { useRouter } from 'next/router'; export default function Author() { const { query } = useRouter(); return ( <div> <h1> Fetch author with book-id <em>{query['book-id']}</em> </h1> </div> ); }

ขยายส่วนเส้นทางแบบไดนามิกด้วย Catch All Routes

คุณเคยเห็นไวยากรณ์วงเล็บเซ็กเมนต์เส้นทางไดนามิกเหมือนในตัวอย่างก่อนหน้าด้วย [book-id].js ความสวยงามของรูปแบบนี้คือมันทำให้ Catch-All Routes ก้าวล้ำไปอีกขั้น คุณสามารถอนุมานได้ว่าสิ่งนี้ใช้ทำอะไรจากชื่อ: มันจับทุกเส้นทาง

เมื่อเราดูตัวอย่างแบบไดนามิก เราได้เรียนรู้วิธีที่ช่วยขจัดความซ้ำซ้อนในการสร้างไฟล์สำหรับเส้นทางเดียวในการเข้าถึงหนังสือหลายเล่มด้วย ID ของพวกเขา แต่มีอย่างอื่นที่เราสามารถทำได้

โดยเฉพาะอย่างยิ่ง เรามีพาธ /printed-books/:book-id โดยมีโครงสร้างไดเร็กทอรี:

 next-app └── pages ├── index.js └── printed-books ├── index.js └── [book-id].js

หากเราอัปเดตพาธให้มีเซ็กเมนต์มากขึ้น เช่น หมวดหมู่ เราอาจลงเอยด้วย: /printed-books/design/:book-id , /printed-books/engineering/:book-id หรือยังคงดีกว่า /printed-books/:category/:book-id . หนังสือ/:หมวดหมู่/:book-id

มาเพิ่มปีที่วางจำหน่าย: /printed-books/:category/:release-year/:book-id กันเถอะ คุณเห็นรูปแบบหรือไม่? โครงสร้างไดเร็กทอรีจะกลายเป็น:

 next-app └── pages ├── index.js └── printed-books └── [category] └── [release-year] └── [book-id].js

เราแทนที่การใช้ไฟล์ที่มีชื่อสำหรับเส้นทางแบบไดนามิก แต่ก็ยังจบลงด้วยความซ้ำซ้อนรูปแบบอื่น มีการแก้ไข: Catch All Routes ที่ไม่จำเป็นต้องใช้เส้นทางที่ซ้อนกันอย่างลึกล้ำ:

 next-app └── pages ├── index.js └── printed-books └── [...slug].js

ใช้ไวยากรณ์วงเล็บเดียวกัน ยกเว้นว่ามีจุดสามจุดนำหน้า ลองนึกถึงจุดต่างๆ เช่น ไวยากรณ์การแพร่กระจาย JavaScript คุณอาจสงสัยว่า: ถ้าฉันใช้เส้นทางที่รับทั้งหมด ฉันจะเข้าถึงหมวดหมู่ได้อย่างไร ( [category] ) และปีที่เผยแพร่ ( [release-year] ) สองทาง:

  1. ในกรณีของตัวอย่างหนังสือที่ตีพิมพ์ เป้าหมายสุดท้ายคือหนังสือ และข้อมูลหนังสือแต่ละเล่มจะมีข้อมูลเมตาแนบมาด้วย หรือ
  2. เซ็กเมนต์ "slug" จะถูกส่งกลับเป็นอาร์เรย์ของพารามิเตอร์การค้นหา
 import { useRouter } from 'next/router'; export default function Book() { const { query } = useRouter(); // There's a brief moment where `slug` is undefined // so we use the Optional Chaining (?.) and Nullish coalescing operator (??) // to check if slug is undefined, then fall back to an empty array const [category, releaseYear, bookId] = query?.slug ?? []; return ( <table> <tbody> <tr> <th>Book Id</th> <td>{bookId}</td> </tr> <tr> <th>Category</th> <td>{category}</td> </tr> <tr> <th>Release Year</th> <td>{releaseYear}</td> </tr> </tbody> </table> ); }

นี่คือตัวอย่างเพิ่มเติมสำหรับเส้นทาง /printed-books/[…slug] :

เส้นทาง พารามิเตอร์แบบสอบถาม
/printed-books/click.js { “กระสุน”: [“คลิก”] }
/printed-books/2020/click.js { “กระสุน”: [“2020”, “คลิก”] }
/printed-books/design/2020/click.js { “กระสุน”: [“การออกแบบ”, “2020”, “คลิก”] }

เช่นเดียวกับเส้นทางที่รับทั้งหมด เส้นทาง /printed-books จะเกิดข้อผิดพลาด 404 เว้นแต่คุณจะระบุเส้นทางดัชนีทางเลือก

 next-app └── pages ├── index.js └── printed-books ├── index.js // path: /printed-books └── [...slug].js

เนื่องจากเส้นทางที่รับได้ทั้งหมดนั้น “เข้มงวด” มันตรงกับตัวทากหรือเกิดข้อผิดพลาด หากคุณต้องการหลีกเลี่ยงการสร้างเส้นทางดัชนีควบคู่ไปกับเส้นทางที่รับทั้งหมด คุณสามารถใช้เส้นทางที่รับทั้งหมดซึ่งเป็น ทางเลือก แทน

การขยายส่วนเส้นทางแบบไดนามิกด้วยตัวเลือก Catch-All Routes

ไวยากรณ์เหมือนกับ catch-all-routes แต่มีวงเล็บเหลี่ยมคู่แทน

 next-app └── pages ├── index.js └── printed-books └── [[...slug]].js

ในกรณีนี้ เส้นทางที่รับทั้งหมด (slug) เป็นทางเลือก และหากไม่มี ทางเลือกสำรองของเส้นทาง /printed-books แสดงผลด้วย [[…slug]].js ตัวจัดการเส้นทาง โดยไม่มีพารามิเตอร์การสืบค้นใดๆ

ใช้ catch-all ควบคู่ไปกับเส้นทางดัชนีหรือเส้นทาง catch-all ทางเลือกเพียงอย่างเดียว หลีกเลี่ยงการใช้เส้นทาง catch-all และตัวเลือก catch-all ควบคู่ไปกับ

ลำดับความสำคัญของเส้นทาง

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

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

ตัวอย่างเช่น การมีเส้นทางแบบไดนามิกมากกว่าหนึ่งเส้นทางในระดับเดียวกันถือเป็นข้อผิดพลาด

 // This is an error // Failed to reload dynamic routes: Error: You cannot use different slug names for the // same dynamic path ('book-id' !== 'id'). next-app └── pages ├── index.js └── printed-books ├── [book-id].js └── [id].js

หากคุณดูเส้นทางที่กำหนดไว้ด้านล่างอย่างใกล้ชิด คุณจะสังเกตเห็นโอกาสที่จะเกิดการปะทะกัน

 // Directory structure flattened for simplicity next-app └── pages ├── index.js // index route (also a predefined route) └── printed-books ├── index.js ├── tags.js // predefined route ├── [book-id].js // handles dynamic route └── [...slug].js // handles catch all route

ตัวอย่างเช่น ลองตอบคำถามนี้: เส้นทางใดจัดการเส้นทาง /printed-books/inclusive-components

  • /printed-books/[book-id].js หรือ
  • /printed-books/[…slug].js .

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

  1. มี ตัวจัดการเส้นทางที่กำหนดไว้ล่วงหน้า ที่สามารถจัดการเส้นทางได้หรือไม่
    • true — จัดการคำขอเส้นทาง
    • false - ไปที่ 2
  2. มี ตัวจัดการเส้นทางแบบไดนามิก ที่สามารถจัดการเส้นทางได้หรือไม่
    • true — จัดการคำขอเส้นทาง
    • false - ไปที่ 3
  3. มีตัวจัดการเส้นทางที่ รับทั้งหมด ที่สามารถจัดการเส้นทางได้หรือไม่
    • true — จัดการคำขอเส้นทาง
    • false - ไม่พบหน้า 404

ดังนั้น /printed-books/[book-id].js จึงเป็นผู้ชนะ

นี่คือตัวอย่างเพิ่มเติม:

เส้นทาง ตัวจัดการเส้นทาง ประเภทเส้นทาง
/printed-books /printed-books เส้นทางดัชนี
/printed-books/tags /printed-books/tags.js เส้นทางที่กำหนดไว้ล่วงหน้า
/printed-books/inclusive-components /printed-books/[book-id].js เส้นทางแบบไดนามิก
/printed-books/design/inclusive-components /printed-books/[...slug].js ทุกเส้นทาง

next/link API

API next/link จะเปิดเผยคอมโพเนนต์ของ Link เป็นวิธีที่เปิดเผยในการดำเนินการเปลี่ยนเส้นทางฝั่งไคลเอ็นต์

 import Link from 'next/link' function TopNav() { return ( <nav> <Link href="/">Smashing Magazine</Link> <Link href="/articles">Articles</Link> <Link href="/guides">Guides</Link> <Link href="/printed-books">Books</Link> </nav> ) }

องค์ประกอบ Link จะแก้ไขเป็นไฮเปอร์ลิงก์ HTML ปกติ นั่นคือ <Link href="/">Smashing Magazine</Link> จะเปลี่ยนเป็น <a href="/">Smashing Magazine</a>

พร็อพ href เป็นพร็อพที่จำเป็นเพียงอย่างเดียวสำหรับคอมโพเนนต์ของ Link ดูเอกสารสำหรับรายการอุปกรณ์ประกอบฉากทั้งหมดที่มีอยู่ในองค์ประกอบ Link

มีกลไกอื่นๆ ขององค์ประกอบ Link ที่ต้องระวัง

เส้นทางที่มีเซกเมนต์ไดนามิก

ก่อนหน้า Next.js 9.5.3 การ Link ไปยังเส้นทางไดนามิกหมายความว่าคุณต้องระบุทั้ง href และ as เสาใน Link เช่นเดียวกับใน:

 import Link from 'next/link'; const printedBooks = [ { name: 'Ethical Design', id: 'ethical-design' }, { name: 'Design Systems', id: 'design-systems' }, ]; export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href="/printed-books/[printed-book-id]" as={`/printed-books/${printedBook.id}`} > {printedBook.name} </Link> )); }

แม้ว่าสิ่งนี้จะทำให้ Next.js สอดแทรก href สำหรับพารามิเตอร์ไดนามิก แต่ก็เป็นเรื่องที่น่าเบื่อหน่าย เกิดข้อผิดพลาดได้ง่าย และค่อนข้างจำเป็น และตอนนี้ได้รับการแก้ไขแล้วสำหรับกรณีการใช้งานส่วนใหญ่ด้วยการเปิดตัว Next.js 10

การแก้ไขนี้ยังเข้ากันได้แบบย้อนหลัง หากคุณเคยใช้ทั้ง as และ href ไม่มีอะไรเสียหาย ในการใช้ไวยากรณ์ใหม่ ให้ละทิ้ง href prop และค่าของมัน และเปลี่ยนชื่อ as prop เป็น href ตามตัวอย่างด้านล่าง:

 import Link from 'next/link'; const printedBooks = [ { name: 'Ethical Design', id: 'ethical-design' }, { name: 'Design Systems', id: 'design-systems' }, ]; export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href={`/printed-books/${printedBook.id}`}>{printedBook.name}</Link> )); }

ดูการแก้ไขอัตโนมัติของ href

Use-cases For The passHref Prop

ลองดูตัวอย่างด้านล่างอย่างใกล้ชิด:

 import Link from 'next/link'; const printedBooks = [ { name: 'Ethical Design', id: 'ethical-design' }, { name: 'Design Systems', id: 'design-systems' }, ]; // Say this has some sort of base styling attached function CustomLink({ href, name }) { return <a href={href}>{name}</a>; } export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href={`/printed-books/${printedBook.id}`} passHref> <CustomLink name={printedBook.name} /> </Link> )); }

อุปกรณ์ประกอบฉาก passHref บังคับให้องค์ประกอบ Link ส่งอุปกรณ์ href ลงไปที่องค์ประกอบย่อยของ CustomLink นี่เป็นข้อบังคับหากส่วนประกอบ Link ตัดรอบส่วนประกอบที่ส่งคืนแท็ก <a> ไฮเปอร์ลิงก์ กรณีการใช้งานของคุณอาจเป็นเพราะคุณกำลังใช้ไลบรารี่เช่น styled-components หรือหากคุณต้องการส่งลูกหลาย ๆ ลูกไปยังองค์ประกอบ Link เนื่องจากคาดว่าจะมีลูกเพียงตัวเดียว

ดูเอกสารเพื่อเรียนรู้เพิ่มเติม

วัตถุ URL

พร็อ href ของคอมโพเนนต์ Link สามารถเป็นออบเจ็กต์ URL ที่มีคุณสมบัติเช่น query ซึ่งจัดรูปแบบเป็นสตริง URL โดยอัตโนมัติ

ด้วยอ็อบเจ็กต์ printedBooks ตัวอย่างด้านล่างจะเชื่อมโยงไปยัง:

  1. /printed-books/ethical-design?name=Ethical+Design and
  2. /printed-books/design-systems?name=Design+Systems .
 import Link from 'next/link'; const printedBooks = [ { name: 'Ethical Design', id: 'ethical-design' }, { name: 'Design Systems', id: 'design-systems' }, ]; export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href={{ pathname: `/printed-books/${printedBook.id}`, query: { name: `${printedBook.name}` }, }} > {printedBook.name} </Link> )); }

หากคุณรวมเซ็กเมนต์ไดนามิกใน pathname คุณต้องรวมเซ็กเมนต์นั้นเป็นคุณสมบัติในออบเจกต์การสืบค้นเพื่อให้แน่ใจว่ามีการสอดแทรกการสืบค้นใน pathname :

 import Link from 'next/link'; const printedBooks = [ { name: 'Ethical Design', id: 'ethical-design' }, { name: 'Design Systems', id: 'design-systems' }, ]; // In this case the dynamic segment `[book-id]` in pathname // maps directly to the query param `book-id` export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href={{ pathname: `/printed-books/[book-id]`, query: { 'book-id': `${printedBook.id}` }, }} > {printedBook.name} </Link> )); }

ตัวอย่างข้างต้นมีเส้นทาง:

  1. /printed-books/ethical-design และ
  2. /printed-books/design-systems .

หากคุณตรวจสอบแอตทริบิวต์ href ใน VSCode คุณจะพบประเภท LinkProps โดยมีคุณสมบัติ href เป็นประเภท Url ซึ่งเป็น string หรือ UrlObject ตามที่กล่าวไว้ก่อนหน้านี้

สกรีนช็อตของประเภท LinkProps ที่ตรวจสอบใน VSCode
การตรวจสอบ LinkProps ใน VSCode (ตัวอย่างขนาดใหญ่)

การตรวจสอบ UrlObject ต่อไปจะนำไปสู่ส่วนต่อประสานกับคุณสมบัติ:

สกรีนช็อตของ <code>UrlObject</code> ที่ตรวจสอบแล้วใน VSCode
การตรวจสอบ UrlObject ใน VSCode (ตัวอย่างขนาดใหญ่)

คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับคุณสมบัติเหล่านี้ได้ในเอกสารประกอบโมดูล Node.js URL

กรณีการใช้งานหนึ่งของแฮชคือการลิงก์ไปยังส่วนต่างๆ ในหน้า

 import Link from 'next/link'; const printedBooks = [{ name: 'Ethical Design', id: 'ethical-design' }]; export default function PrintedBooks() { return printedBooks.map((printedBook) => ( <Link href={{ pathname: `/printed-books/${printedBook.id}`, hash: 'faq', }} > {printedBook.name} </Link> )); }

ไฮเปอร์ลิงก์จะแก้ไขเป็น /printed-books/ethical-design#faq

เรียนรู้เพิ่มเติมในเอกสาร

API next/router

หาก next/link เป็นแบบเปิดเผย แสดงว่าตัว next/router มีความจำเป็น มันเปิดเผย useRouter hook ที่อนุญาตให้เข้าถึงวัตถุ router ภายในองค์ประกอบฟังก์ชั่นใด ๆ คุณสามารถใช้เบ็ดนี้เพื่อกำหนดเส้นทางด้วยตนเอง โดยเฉพาะอย่างยิ่งในบางสถานการณ์ที่ next/link ไม่เพียงพอ หรือตำแหน่งที่คุณต้องการ "เชื่อมต่อ" กับการกำหนดเส้นทาง

 import { useRouter } from 'next/router'; export default function Home() { const router = useRouter(); function handleClick(e) { e.preventDefault(); router.push(href); } return ( <button type="button" onClick={handleClick}>Click me</button> ) }

useRouter เป็น React hook และไม่สามารถใช้กับคลาสได้ ต้องการอ็อบเจ็กต์ router ในคอมโพเนนต์คลาสหรือไม่ ใช้กับเรา withRouter

 import { withRouter } from 'next/router'; function Home({router}) { function handleClick(e) { e.preventDefault(); router.push(href); } return ( <button type="button" onClick={handleClick}>Click me</button> ) } export default withRouter(Home);

วัตถุ router

ทั้ง useRouter hook และส่วนประกอบที่มีลำดับสูงกว่า withRouter ส่งคืนอ็อบเจ็กต์เราเตอร์ที่มีคุณสมบัติเช่น pathname , query , asPath และ basePath ที่ให้ข้อมูลเกี่ยวกับสถานะ URL ของหน้าปัจจุบัน locale , locales และ defaultLocale ที่ให้ข้อมูลเกี่ยวกับ ใช้งานอยู่ รองรับ หรือโลแคลเริ่มต้นปัจจุบัน

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

เรียนรู้เพิ่มเติมเกี่ยวกับวัตถุเราเตอร์

การกำหนดค่าเส้นทางที่กำหนดเองด้วย next.config.js

นี่เป็นโมดูล Node.js ปกติที่สามารถใช้กำหนดค่าพฤติกรรม Next.js บางอย่างได้

 module.exports = { // configuration options }

อย่าลืมรีสตาร์ทเซิร์ฟเวอร์ทุกครั้งที่คุณอัปเดต next.config.js เรียนรู้เพิ่มเติม.

เส้นทางฐาน

มีการกล่าวถึงเส้นทางเริ่มต้น/เริ่มต้นใน Next.js คือ pages/index.js พร้อมเส้นทาง / สิ่งนี้สามารถกำหนดค่าได้ และคุณสามารถกำหนดเส้นทางเริ่มต้นเป็นเส้นทางย่อยของโดเมนได้

 module.exports = { // old default path: / // new default path: /dashboard basePath: '/dashboard', };

การเปลี่ยนแปลงเหล่านี้จะมีผลในแอปพลิเคชันของคุณโดยอัตโนมัติด้วยเส้นทางทั้งหมด / เส้นทางไปยัง /dashboard

ฟีเจอร์นี้ใช้ได้กับ Next.js 9.5 ขึ้นไปเท่านั้น เรียนรู้เพิ่มเติม.

ต่อท้ายเฉือน

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

 module.exports = { trailingSlash: true };
 # trailingSlash: false /printed-books/ethical-design#faq # trailingSlash: true /printed-books/ethical-design/#faq

ทั้งคุณสมบัติพาธพื้นฐานและสแลชต่อท้ายสามารถใช้ได้กับ Next.js 9.5 ขึ้นไปเท่านั้น

บทสรุป

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

แหล่งข้อมูลที่เกี่ยวข้อง

  • เอกสารอย่างเป็นทางการของ Next.js สำหรับ Pages
  • เอกสารทางการของ Next.js สำหรับการดึงข้อมูล
  • เอกสารอย่างเป็นทางการของ Next.js สำหรับ next.config.js
  • Next.js 10: การแก้ไข href . โดยอัตโนมัติ
  • เอกสารอย่างเป็นทางการของ Next.js สำหรับ next/link
  • เอกสารอย่างเป็นทางการ Next.js สำหรับ next/router