มีอะไรใหม่บ้างใน Next.js 13.2

Nuttavut Thongjor

App Router เป็นฟีเจอร์ใหม่ใน Next 13 ที่ช่วยให้เราจัดการระบบ Routes และ Layout ได้ดียิ่งขึ้นผ่านโฟลเดอร์ app แม้ตอนนี้ฟีเจอร์ดังกล่าวจะยังอยู่ในสถานะ Beta แต่ความสามารถใหม่ ๆ ก็ถูกเพิ่มมาเรื่อย ๆ ไม่เว้นแม้แต่ในเวอร์ชั้น 13.2 ด้วย

Metadata และการทำ SEO

เราต่างทราบกันดีว่า meta tag มีผลต่อการทำ SEO สำหรับ Next.js 13.2 นั้นได้เพิ่ม Metadata เข้ามาเพื่อช่วยประกาศค่า meta นี้ โดยเราสามารถสร้างตัวแปรชื่อ metadata แล้วคืนกลับจากไฟล์ pages.ts หรือ layout.ts ได้ เช่น ถ้าต้องการให้ทุก ๆ หน้าของเพจเรามี title และ description เป็น My Shopping App และ My Shopping App Desc ตามลำดับ ให้ประกาศตัวแปร metadata ในไฟล์ app/layout.ts ดังนี้

TypeScript
1export const metadata = {
2 title: 'My Shopping App',
3 description: 'My Shopping App Desc',
4};
5
6type RootLayout = {
7 children: ReactNode;
8};
9
10export default function RootLayout({ children }: RootLayout) {
11 return (
12 <html lang="en">
13 <body>{children}</body>
14 </html>
15 );
16}

จากการแก้ไขดังกล่าวเป็นผลให้เมื่อเปิดเพจใด ๆ จะปรากฎค่าทั้งสองในส่วนของ head ดังนี้

HTML
1<html lang="en">
2 <head>
3 <title>My Shopping App</title>
4 <meta name="description" content="My Shopping App Desc" />
5 <!-- อื่น ๆ -->
6 </head>
7 <!-- อื่น ๆ -->
8</html>

metadata นั้นสามารถปรับเปลี่ยนได้ในแต่ละหน้าเพจ หากค่าใดถูกกำหนดใหม่ใน pages.ts หรือ layout.ts ของเพจปลายทาง ค่าใหม่นั้นจะถูกใช้งานแทน

สมมติโครงสร้างโปรเจคเราเป็นดังนี้

Metadata Project Layout

เมื่อเราเข้าสู่หน้า /products/:id เราต้องการแก้ไขค่า meta ใหม่จากเดิมที่กำหนดแบบเหมารวมไว้ใน app/layout.ts ให้ได้ผลลัพธ์เป็นค่าดังนี้

HTML
1<html lang="en">
2 <head>
3 <title>My Product Details</title>
4 <meta name="description" content="My Product Details Desc" />
5 <!-- อื่น ๆ -->
6 </head>
7 <!-- อื่น ๆ -->
8</html>

เราสามารถเพิ่มตัวแปร metadata พร้อมคืนกลับผ่าน export จาก app/products/[id]/pages.ts ได้เลย ดังนี้

TypeScript
1export const metadata = {
2 title: 'Product Details',
3 description: 'Product Details Desc',
4};
5
6export default function ProductDetails() {
7 return <div>Product Details</div>;
8}

สำหรับหน้าเพจ /products/1 เราอาจต้องการแสดง title เป็น Product: 1 และส่วนของ description เป็นชื่อของสินค้ามากกว่า ส่วนนี้จะแปรเปลี่ยนไปตาม id ของ Product เราจึงต้องการฟังก์ชันพิเศษ ที่สามารถสร้าง Metadata ที่เปลี่ยนแปลงได้อย่างยืดหยุ่น และนั่นคือความสามารถของฟังก์ชันใหม่คือ generateMetadata

TypeScript
1import { Metadata } from 'next';
2
3type ProductParams = {
4 id: number;
5};
6
7async function getProduct(id: ProductParams['id']) {
8 const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
9
10 return res.json();
11}
12
13export async function generateMetadata({
14 params,
15}: {
16 params: ProductParams;
17}): Promise<Metadata> {
18 const { id, title } = await getProduct(params.id);
19
20 return { title: `Product: ${id}`, description: title };
21}

จากโค้ดดังกล่าวเมื่อส่วนของ params คือ id ใน URL เปลี่ยนเราก็จะได้ title และ description ที่ปรับเปลี่ยนใหม่ตามเช่นกัน

HTML
1<html lang="en">
2 <head>
3 <title>Product: 1</title>
4 <meta
5 name="description"
6 content="sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
7 />
8 <!-- อื่น ๆ -->
9 </head>
10 <!-- อื่น ๆ -->
11</html>

ส่วนของ Metadata นั้นสามารถเพิ่ม meta tag อื่น ๆ ได้ เช่น Open Graph

TypeScript
1export async function generateMetadata({
2 params,
3}: {
4 params: ProductParams;
5}): Promise<Metadata> {
6 const { id, title } = await getProduct(params.id);
7
8 return {
9 title: `Product: ${id}`,
10 description: title,
11 openGraph: {
12 title: `Product: ${id}`,
13 description: title,
14 url: `https://my-shopping.com/products/${id}`,
15 },
16 };
17}

โค้ดข้างต้นให้ผลลัพธ์เป็น HTML ดังนี้

HTML
1<html lang="en">
2 <head>
3 <title>Product: 1</title>
4 <meta
5 name="description"
6 content="sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
7 />
8 <meta property="og:title" content="Product: 1" />
9 <meta
10 property="og:description"
11 content="sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
12 />
13 <meta property="og:url" content="https://my-shopping.com/products/1" />
14 <!-- อื่น ๆ -->
15 </head>
16 <!-- อื่น ๆ -->
17</html>

การสร้าง Routes สำหรับ API

แต่เดิมนั้น Next.js มีความสามารถในการสร้าง API ผ่านโฟลเดอร์ pages/api อยู่แล้ว แต่เมื่อเข้าสู่ยุคของ App Router ที่ใช้โฟลเดอร์ app เป็นหลัก ฟีเจอร์นี้กลับขาดหายไป

Next 13.2 ได้กู้ความสามารถนี้กลับมา เมื่อเราสร้างไฟล์ชื่อ routes.ts เราจะสามารถประกาศ API Routes ได้อีกครั้ง สิ่งที่เราต้องทำคือการ export HTTP Verbs ที่เราต้องการ เช่นเมื่อต้องการสร้าง Routes สำหรับ /api/products โดยสนับสนุนทั้ง HTTP GET และ HTTP POST เราต้อง export ฟังก์ชันชื่อ GET และ POST จากไฟล์ route.ts ดังนี้

TypeScript
1export async function GET(request: Request) {
2 const products = [
3 { id: 1, title: 'Hello' },
4 { id: 2, title: 'World' },
5 ];
6
7 return Response.json({ data: products });
8}
9
10export async function POST(request: Request) {
11 return new Response('Created!', { status: 201 });
12}

ไฟล์ดังกล่าวนั้นจะถูกประกาศไว้ที่โฟลเดอร์ app/products/route.ts

นอกเหนือจาก GET และ POST แล้ว Next ยังสนับสนุน HTTP Verbs อื่นอีกได้แก่ GET, HEAD, OPTIONS, POST, PUT, DELETE, และ PATCH

Statically Typed Links

Next 13.2 มาพร้อมกับความสามารถในการป้องกันการพิมพ์ path ของ Link ผิด สมมติให้โครงสร้างโปรเจคของเราเป็นดังนี้

Metadata Project Layout

จากโครงสร้างดังกล่าวพบว่ามีเฉพาะ path คือ / และ /products/:id ที่เป็นไปได้ ดังนั้นเมื่อเราใช้คอมโพแนนท์ Link ด้วย path ที่ผิด เช่น <Link href='/foo'>My Link</Link> Next ก็จะแจ้งเตือนความผิดพลาดนี้ให้ทราบ

เพื่อให้ความสามารถนี้ใช้ได้มีสามสิ่งที่เราต้องทำก่อน

  1. โปรเจคนี้ต้องใช้ภาษา TypeScript
  2. โปรเจคนี้ต้องใช้โฟลเดอร์ app แทน pages
  3. ทำการตั้งค่า typedRoutes: true ใน next.config.js
JavaScript
1/** @type {import('next').NextConfig} */
2const nextConfig = {
3 experimental: {
4 appDir: true,
5 typedRoutes: true,
6 },
7};
8
9module.exports = nextConfig;

ปรับปรุงการแสดงผลข้อผิดพลาด

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

Error Overlay

MDX บน Server Component

Next.js 13.2 การทำงานกับ MDX จะเกิดขึ้นบน React Server Component ทั้งหมด นั่นทำให้ขนาด JavaScript บน Client-Side โดยรวมลดลงและโหลดเร็วขึ้น นอกจากนี้ปลั๊กอิน @next/mdx ยังเพิ่มความสามารถด้วยการสนับสนุนให้เราสร้างและแก้ไขคอมโพแนนท์ใน MDX ได้เองผ่านไฟล์ mdx-components.ts อีกด้วย

ขั้นตอนการใช้งาน MDX บน Next.js สามารถอ่านเพิ่มเติมได้จาก การใช้งาน MDX บน Next.js

สรุป

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

เอกสารอ้างอิง

Next.js 13.2. Retrieved Feb, 25, 2023, from https://nextjs.org/blog/next-13-2 Next.js 13.2 Explained!. Retrieved Feb, 25, 2023, from https://www.youtube.com/watch?v=UfNMlhu3L4I

สารบัญ

สารบัญ

  • Metadata และการทำ SEO
  • การสร้าง Routes สำหรับ API
  • Statically Typed Links
  • ปรับปรุงการแสดงผลข้อผิดพลาด
  • MDX บน Server Component
  • สรุป
  • เอกสารอ้างอิง