Step 1 Install จ้า (roitai คือชื่อโฟลเดอร์)
npx create-next-app@latest roitai
/app
/about
/page.tsx # http://localhost:3000/about
/info
/page.tsx # http://localhost:3000/info
[id] # [id] params folder
/page.tsx # http://localhost:3000/info/123456
/_folder # Private Folder (no public access)
/(auth) # Groups Folder
/login
/page.tsx # http://localhost:3000/login
/[...sign-in] # params
/page.tsx # http://localhost:3000/sign-in
/register
/page.tsx # http://localhost:3000/register
import type { Metadata } from "next" ;
export const metadata : Metadata = {
title : "Roitai" ,
description : "Camping in Thailand." ,
keywords : "Camping, Thailand, Roitai" ,
} ;
Default เป็น Server Component
SEO ที่ดี (Search Engine Optimization)
ความปลอดภัย
Caching
ดึงข้อมูลจาก database ได้
ถ้าจะใช้ต้องเพิ่ม 'use client'
Client Components สามารถใช้ state, effects และ event listeners
สามารถใช้ Browser APIs เช่น geolocation หรือ localStorage
const url = "https://jsonplaceholder.typicode.com/todos" ;
const url =
"https://fastly.picsum.photos/id/0/5000/3333.jpg?hmac=_j6ghY5fCfSD6tvtcV74zXivkJSPIfR9B8w34XeQmvU" ;
import type { NextConfig } from "next" ;
const nextConfig : NextConfig = {
/* config options here */
images : {
remotePatterns : [
{
protocol : "https" ,
hostname : "fastly.picsum.photos" ,
pathname : "/**" ,
} ,
] ,
} ,
} ;
export default nextConfig ;
Next.js component
ช่วยให้การจัดการภาพในเว็บไซต์มีประสิทธิภาพมากขึ้นโดยอัตโนมัติ
ปรับขนาดภาพให้เหมาะสม ลดการเลื่อนเลย์เอาต์ และเพิ่มความเร็วในการโหลดหน้าเว็บ
export default const ServerComponent = ( ) => {
const myAction = async ( formData ) => {
'use server'
// จัดการข้อมูลจาก form -> formData.get('name')
// CRUD ข้อมูล -> mutate data (server)
// รีเฟรชข้อมูล -> revalidate cache
}
return < form action = { myAction } > ... </ form >
}
'use client'
import { myAction } from './action'
export default const ClientComponent = ( ) => {
return (
< form action = { myAction } >
< button type = 'submit' > Add </ button >
</ form >
)
}
export const createCamp = async ( formData ) => {
// const firstName = formData.get('title')
// const description = formData.get('description')
const rawData = Object . fromEntries ( formData ) ;
console . log ( rawData ) ;
// db.camp.create({})
// revalidatePath('/actions') // refresh Data
// redirect('/')
} ;
export const fetchCamp = async ( ) => {
// db.camp.findMany({})
const user = [
{ id : 1 , title : "Route 3060" } ,
{ id : 2 , title : "Korat" } ,
] ;
return user ;
} ;
const [ state , formAction , isPending ] = useActionState ( fn , initialState , permalink ?) ;
const { pending, data, method, action } = useFormStatus ( ) ;
import { fetchCamp } from "@/utils/actions" ;
import { NextResponse } from "next/server" ;
export const GET = async ( req : NextResponse ) => {
const { searchParams } = new URL ( req . url ) ;
const id = searchParams . get ( "id" ) ;
console . log ( id ) ;
const camps = await fetchCamp ( ) ;
// return Response.json({ camps });
return NextResponse . redirect ( new URL ( "/" , req . url ) ) ;
} ;
import { NextResponse } from "next/server" ;
export function middleware ( req : Request ) {
console . log ( "hello middleware" ) ;
return NextResponse . redirect ( new URL ( "/" , req . url ) ) ;
}
export const config = {
// matcher : '/counter'
matcher : [ "/about/:path*" , "/counter/:path*" ] ,
} ;
เมื่อไม่กำหนด type TypeScript จะมองว่า data เป็น any ซึ่งหมายความว่าไม่มีการตรวจสอบชนิดข้อมูลเลย
โค้ดอ่านง่ายขึ้น เพราะเห็นโครงสร้างข้อมูลชัดเจน
ลดข้อผิดพลาด เพราะ TypeScript ช่วยตรวจสอบและแนะนำการใช้งาน property