Share on social media

next-start สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video

Next คืออะไร ?

Ref: https://nextjs.org/docs

Next.js คือ React framework ที่สร้างขึ้นมาเพื่อทำ full-stack web application. โจทย์หลักของ Next (ขอเรียกย่อจาก Next.js) คือ

  1. ใช้ React framework เป็น core หลักในการทำ (สามารถใช้เครื่องมือ debug React ทั้งหมดในการจัดการได้)
  2. เพิ่มความสามารถจาก Frontend Framework ของ React ให้ทำงานร่วมกับฝั่งของ Server side ได้ (สามารถที่จะ render ข้อมูลก่อนที่จะออกมาที่ Frontend ได้) โดยได้เตรียมของที่จำเป็นสำหรับการจัดการระหว่างฝั่ง Client - Server เอาไว้เรียบร้อย
  3. Optimization สามารถทำการ optimization code เพื่อให้ได้ Performance สูงสุดของ web ออกมาได้

มาดู Feature หลักๆของ Next กันว่ามีอะไรบ้าง

  1. Routing = สามารถจัดการ Router ผ่าน file-system based ออกมาได้ (วาง file = ได้ route)
  2. Rendering = สามารถทำ Client-side and Server-side Rendering with Client and Server Components (รวมถึงการทำ Dynamic rendering ระหว่าง client - server ได้ด้วย)
  3. Data fetching = สามารถจัดการการดึงข้อมูลผ่าน fetch ได้
  4. Styling = support หมดทั้ง CSS Module (SASS), Tailwind, CSS-in-JS
  5. Optimization = build code + asset เพื่อ optimize ผลลัพธ์ที่จะนำไปขึ้น deploy
  6. Support Typescript

สิ่งที่เราจะมาลองใน Session นี้

Agenda

  1. Setup project next
  2. การใช้ component ใน Next
  3. App Routing ของ next (ทำ route แต่ละหน้า)
  • ปรัชญาระหว่าง App Router และ Pages router
  1. Data fetching
  • รู้จักกับ Server component และ Client Component
  1. Deploy ขึ้น vercel

เราจะมาทำเว็บ blog กันโดยยังคงใช้ mockapi เป็นแหล่งของ API ในการทำ

Setup project

Ref: https://nextjs.org/docs/getting-started/installation

Terminal window
npx create-next-app@latest

และเลือกใช้ค่า default ทั้งหมด next-basic-01

  • เพื่อความงดงามและให้แต่ง style ง่ายขึ้น เราจะเลือกใช้ tailwind เพิ่มเข้ามา

start project

Terminal window
npm run dev

เว็บจะโดนเปิดมาที่ port 3000 เป็นค่า default

Routing

Ref: https://nextjs.org/docs/app/building-your-application/routing/defining-routes

Routing คือการจัดการ web app ให้ออกมามี path ที่ถูกต้องตามที่เราต้องการได้

ข้อสังเกตุแรก

  • ตรงส่วนด้านบนมีปรับ App Router และ Pages Router คู่กันเอาไว้

ท่าการทำ routing ใน Next มี 2 ท่าคือ

  • Pages Router = ใช้ folder pages ในการ control route (เป็นค่า default เก่าตั้งแต่ Next version ก่อน 14)
  • App Router = ใช้ folder app ในการ control pages (เป็นท่า default ปัจจุบัน)

Reminder ทุกท่าที่เราจะใช้ในนี้จะเป็นท่าของ App Router (เนื่องจาก Next.js support ท่านี้เป็น default ไว้แล้ว)

  • หากใครยังมีความจำเป็นใช้ Pages Router อยู่ ฟัง concept แล้วลองกลับ document ดูได้
  • ถ้าใครเริ่มใหม่ (ตั้งแต่ Next.js 14 เป็นต้นไป) = ใช้ท่า App Router แทน

โดยเราจะต้อง follow pattern ตามนี้คือ

  1. ทุกอย่างต้องสร้างภายใต้ folder app โดยอยากได้ path ไหนต้องระบุชื่อ folder นั้น
router-1
  1. ภายใน folder path นั้นๆ จะต้องสร้างไฟล์ชื่อ page.js เพื่อให้เป็นจุดเริ่มต้นของ path เสมอ (ถ้าไม่สร้าง = link มาหา path นั้นไม่ได้)
router-2
  1. หากใครใช้เป็น typescript = .tsx, ใครใช้เป็น javascript ปกติ = .js

เพียงเท่านี้ก็จะสามารถสร้าง path ขึ้นมาได้

Dynamic route

Ref: https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes

เราสามารถทำ parameter (จากนี้ใน docs จะขอเรียก param) กับ routing ได้ผ่านวิธีการดังนี้

  1. สร้างชื่อ folder เหมือนกับชื่อ param ได้โดยการตั้งชื่อ folder [slug]

  2. เราสามารถเรียกผ่านตัวแปร params ได้จาก Component ของ page.js ได้เลย

ex. path ชื่อ app/blog/[slug]/page.js

และ code เป็น

export default function Page({ params }) {
return <div>My Post: {params.slug}</div>;
}

เมื่อเปิดผ่าน /blog/a = ก็จะได้เป็น { slug: 'a' } ใน params ออกมาได้

ท่าอื่นเพิ่มเติม: Catch-all Segments

  • Ref: https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments
  • กรณีที่เราจะเอาทุก path เลย (หลังจากที่มีการกำหนด path ไว้) = ท่านี้จะทำการ get ทุก path ไว้หมด
  • เช่น เคส /blog ถ้าใช้วิธีนี้ ไม่ว่าจะเป็น /blog/1, /blog/test/2, /blog/m/1 ก็จะเข้าเงื่อนไขนี้ทั้งหมดได้

Route Handlers

Ref: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#dynamic-functions

  • ใน Next เนื่องจากเป็นการ render ที่สามารถทำได้ตั้งแต่ ฝั่ง Server มา = สามารถทำ API กับ App router ได้

โดยข้อตกลงคือ

  1. สร้าง folder ที่ต้องการทำ path API

  2. สร้าง file ชื่อ route.js เพื่อทำการเพิ่ม logic ของ api เข้าไป (จะสามารถ control ตัวแปร Request, Response ออกมาได้)

api-01

เช่น สมมุติ

  • เราสร้าง file ที่ path app/test/route.js

และใน route.js มี code ตามนี้

export async function GET() {
console.log("test");
return Response.json({
name: "mikelopster",
});
}

เมื่อเปิดด้วย /test ก็จะได้ JSON ออกมาได้

next-api-01

เท่ากับเราสามารถทำ API Server ได้ผ่านตัวของ Next นั่นเอง

เพิ่มเติม

  • ชื่อ functions GET() คือ HTT Verb สามารถเปลี่ยนตามการใช้งานได้ เป็น GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS
  • สามารถเพิ่มสิ่งที่เรียกว่า cache ไปได้ผ่าน API (ควบคุมผ่านตัวแปร revalidate)
  • สามารถทำ Dynamic functions โดยการเรียกใช้ cookies และ headers ได้ = สามารถอ่าน / เขียน header, cookie ได้
  • สามารถรับ params มาด้วยไอเดียเดียวกันกับ page ได้
export async function GET(request, { params }) {
const slug = params.slug; // 'a', 'b', or 'c'
}

Middleware

https://nextjs.org/docs/app/building-your-application/routing/middleware

Middleware คือส่วนของ code ที่จะ run ก่อนที่ request จะทำงานเสร็จ

  • สามารถที่จะ rewriting, redirecting, modifying ตัว request, response ก่อนที่จะไปถึงตำแหน่งจริงๆได้

กฎคือ สร้างไฟล์ชื่อ middleware.js ไว้ที่ root project (level เดียวกันกับ app) = ได้ middleware แล้วเรียบร้อย

ตัวอย่าง middleware.js

import { NextResponse } from "next/server";
// This function can be marked `async` if using `await` inside
export function middleware(request) {
return NextResponse.redirect(new URL("/hello", request.url));
}
export const config = {
matcher: "/blog/:path*",
};

เคสที่เราจะมีโอกาสได้ใช้ Middleware

  • Level Access Control = เฉพาะคนที่อนุญาตถึงจะเปิดได้
  • User data = แนบ data user เข้ากับ request ไป จะได้ไม่ต้องเรียกใช้งานจากหลายที่

Note

  • ลองสร้างหน้าของ content เพิ่มมา
  • link ไปยังหน้า content นั้น (Dynamic Routing)
  • ลองทำ Mock API ผ่าน Route Handler
  • ลองทดสอบปิดกั้นผ่าน Middleware

Basic component / Layout / Styling

Ref: https://nextjs.org/docs/app/building-your-application/styling/css-modules https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts

  • อะไรที่ React ทำได้ยังคง support เหมือนเดิม (เช่น css module)
  • เพิ่มเติมคือ CLI มีเพิ่ม tailwind มา สามารถใช้งาน tailwind คู่กันได้ (เป็น default)
  • Support การทำ Layout เป็น default (สามารถระบุชื่อ file layout.js) ในตำแหน่งนั้นๆ
layout-01

โดย layout อยู่ folder ไหนก็จะ support layout ของ folder นั้น

layout-02 layout-03

Note

  • ทดสอบใช้ style tailwind
  • เพิ่ม layout component (header / footer โดยประมาณ)

Rendering

มาทำความเข้าใจเรื่องของ Server component และ Client component ก่อน

  • Client Component = React component ที่ทำการ render จากฝั่ง Client เหมือนปกติ
  • React Server Component (RSC) = คือ component ที่อนุญาตให้เราเขียน UI ที่สามารถ render และ cache บางส่วนไว้บน server ได้ (นั่นคือ เป็นการ run จาก server)

Note: React Server Component คือ Default ของ Component ใน Next.js

  • สำหรับการแยกส่วน Client Component และ Server Component สามารถใช้ use ในการแยกได้
  • ถ้าใช้เป็น Client component ล้วนๆ = เรียกใช้ use client
  • ถ้าใช้เป็น Server component ล้วนๆ = เรียกใช้ use server
  • ทุก React component ที่มีการจัดการ state = ต้องเป็น Client component
  • ถ้าตัวแม่เป็น Client component อยู่แล้ว = ไม่ต้องไล่ประกาศที่ตัว child component ทุกตัวจะถือเป็น client component ทั้งหมดได้

Server component

Ref: https://nextjs.org/docs/app/building-your-application/rendering/server-components

โจทย์หลักๆของ Server component

  • Data Fetching (เดี๋ยวเราจะหยิบทำในหัวข้อถัดไปอีกที)
  • Security = Logic จะอยู่ในฝั่ง server แทน (สามารถเรียกใช้ API key, token จากจุดนี้ได้ มันจะไม่โผล่ไปฝั่ง client)
  • Caching = สามารถเก็บ cache บน server ไว้ได้
  • เพิ่มความเร็ว First Contentful Paint (FCP)
  • เพิ่มการเจอของ SEO เนื่องจาก content โดน render ออกมาหน้าเว็บ = ทำให้ bot หาเจอได้ (พวก Metadata)

หลักการของ Server component

  1. React จะ render Server component เป็น data format ใหม่ชื่อ “React Server Component Payload (RSC Payload)”
  2. Next.js จะใช้ “RSC Payload” render ที่ Client Component ออกมาที่หน้าเว็บ

ที่เหลือ

  1. HTML ก็จะแสดงออกมาที่หน้าเว็บ
  2. RSC Payload จะถูกใช้สร้าง DOM Tree > และสามารถทำให้หน้าเว็บ interactive ได้ผ่านการ hydrate
loading-01

เพราะฉะนั้น Server component จะเหมาะกับ โจทย์ที่ต้องมีการดึง Data โดยเฉพาะ

Client component

Ref: https://nextjs.org/docs/app/building-your-application/rendering/client-components

Client component คือ React component เลย

  • จัดการ state ทุกอย่างอยู่ที่นี่
  • interactive กับ javascript กับเว็บก็อยู่ที่นี่

เช่น

"use client";
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}

โจทย์หลักๆของ Client component

  • สร้าง UI component ที่ reuse ได้ (ไม่สามารถใช้ setState กับฝั่ง Server component ได้จะเกิด error ทันที)

เหตุแห่งการต้องใช้ use client example-client-component

ตัวอย่าง Error เมื่อไม่ประกาศ use client ใน UI Component

client-01

การประกาศ Server / Client Component ที่ถูกต้อง

Ref: https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns

component-02

Note

  • เพิ่มเติมการเขียน cookie ลง API
  • และฝั่ง middleware อ่านผ่าน cookie API

Data fetching

Ref: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating

Data fetching ว่าด้วยเรื่องของการดึงข้อมูลจาก API ภายนอกมาใช้งาน

  • ใน Server component เราสามารถจัดการดึงข้อมูลมาได้ผ่าน fetch (จะลง axios หรือดึงผ่าน proxy api อย่าง Route Handlers ที่เราทำไปก็ได้)
  • มี feature ที่สามารถทำ Cache ได้ (ยังไม่ลงใน Session นี้) ซึ่ง Data fetching จะมี feature อย่าง Revalidating Data ที่สามารถจัดการ cache ได้

Ref: https://nextjs.org/docs/app/building-your-application/data-fetching/patterns

ซึ่งจุดแข็งแกร่งของ Next.js ที่ Fetch data จาก Server เลยคือ

  1. Access backend data resource ได้เลย
  2. จัดการทั้งหมดผ่าน code ชุดเดียวกันได้ = ลด client-server waterfalls ได้
waterfall-01

Note

  • ลอง data fetch มาแสดงผลก่อน

การ Streaming

Ref: https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming

ปัญหา classic อย่างหนึ่งของการดึงข้อมูลผ่าน Server อย่างพวก Server side render คือการที่ต้องรอข้อมูลจาก server ให้เสร็จก่อน ถึงจะไปต่อได้

อ้างอิงภาพนี้อีกรอบ

loading-01

สิ่งที่มันจะเกิดขึ้นตอน load คือ

  1. หน้าเว็บจะไม่มี content อะไรออกมาก่อนสักแปบ
  2. หลังจาก server load เสร็จ = render หน้าเว็บออกมาทีเดียว
loading-02

ทางแก้ของเรื่องนี้คือการใช้ Streaming

  • Straming คือการ breakdown page มาเป็นส่วนเล็กๆแล้วค่อยทยอย load ไปทีละส่วน
  • จะเน้นให้หน้าเว็บแสดงผลออกมาให้ได้ก่อน แล้วค่อยนำข้อมูลมาแสดงหลังจาก load เสร็จ
  • จะส่งผลทำให้ FCP (display ครั้งแรก) ออกมาไวมาก
streaming-01

และนี่คือการ load ที่เกิดขึ้นหลังจากใช้ Streaming

streaming-02

ซึ่งสิ่งนี้สามารถทำได้ด้วย component <Suspense>

  • สามารถทำได้โดยการ wrap Suspense component
  • หรือสามารถใส่ loading ในการทำได้

Note

  • ดึง data จาก mockapi มา
  • เพิ่ม loading เข้าไป

Server action

Ref: https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations

Server action = การเพิ่ม action server เข้าไปใน component โดยไม่จำเป็น “ต้องสร้าง API แยกออกมา”

  • สามารถเรียกใช้งานได้ทั้ง Server component และ Client component (แต่ต้องประกาศการเรียกใช้ไว้)

ไอเดียการใช้งานคู่กับ page

export default function Page() {
async function create(formData) {
"use server";
// mutate data
// revalidate cache
}
return <form action={create}>...</form>;
}

กลับมาลอง code ตั้งแต่โจทย์ใหม่

สิ่งที่เราจะทำ Server component

  • ทำดึงข้อมูล blog list ออกมา (/)
  • ดึงข้อมูล blog แต่ละหน้า (/blog/:id )
  • เพิิ่ม loading / layout

Client component

  • หน้า Login (+ Server action) + เขียน jwt token สำหรับการ login (/login)
  • เพิ่ม middleware สำหรับการเช็ค path manage ให้เฉพาะคน login เท่านั้น
  • เพิ่มหน้า manage (/manage/blog) สำหรับการ list ทั้งหมด
  • หน้า edit (/manage/blog/:id ) สำหรับแก้ไข blog

อื่นๆเพิ่มเติม

ในหัวข้อนี้เราอาจจะยังไม่ครอบคลุมเรื่อง

  1. Auth เนื่องจากมี in detail ค่อนข้างเยอะ (เราจะกลับมาพร้อมกับหัวข้อที่ทำให้ทุกคนเห็นภาพเรื่อง auth จริงๆแน่นอน)

  2. Cache โดยปกติจะมี 3 strategy ใหญ่ๆคือ

  • Static Rendering
  • Dynamic Rendering
  • Streaming

Ref:

Code: https://github.com/mikelopster/next-basic


Related Post

Share on social media