# [Learn Next 14 重點整理](https://nextjs.org/learn?utm_source=next-site&utm_medium=navbar&utm_campaign=home) ## [搭配 Chatbun 輔助學習](https://chatbun.ai/chatbots/nextjs_export) 使用方式:簡單來說,這是基於 next 14 官網資料的 ai 聊天機器人,有想知道的就可以直接問它,但資料量不多,所以需要搭配這篇重點整理去問,例如: next js 的資料夾結構? next js 如何處理個人驗證? ![截圖 2023-11-10 下午5.00.03](https://hackmd.io/_uploads/Hk5mZ_oQT.png) ## 資料夾結構 ![strutures](https://nextjs.org/_next/image?url=%2Flearn%2Fdark%2Flearn-folder-structure.png&w=1920&q=75&dpl=dpl_BwAwEtN7ncXAnnwFzTiU7xDupY2g) 1. /app: 主要的資料夾 2. /app/lib: 放hooks, utils 3. /app/ui: UI 元件 4. /public: 圖片 靜態資源 5. /scripts/: 資料庫 6. global.css: reset.css 全域 css 規則 7. 條件判斷的 css 可用 clsx 套件 8. 字型 font.ts 9. 圖片 < Image> 元件 10. 如果要優化 外部圖片 or 本地字型 [看這篇最下面](https://nextjs.org/learn/dashboard-app/optimizing-fonts-images) ## 路由 1. 巢狀路由 ![image.png](https://hackmd.io/_uploads/BJFgokHQa.png) 2. 以 page.tsx 為主軸,例如 /dashboard/page.tsx => 頁面 /dashboard 3. /dashboard 中所有 page 的共用元件,則放在 /dashboard/layout.tsx,dashboard 的子頁面時,共用元件就不需要 re-render ([partial-rendering](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#3-partial-rendering)) 4. < Link> 元件 切換頁面時有 SPA 的效果,而且在載入 Link 元件時,會同步 prefetch 他的對應頁面,達到效能優化的效果。 5. usePathname 或任何 react 的 hook 都需要在 'use client' 環境使用 6. 可以使用 clsx 套件做 active 的樣式 ```typescript 'use client'; import { UserGroupIcon, HomeIcon, DocumentDuplicateIcon, } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import clsx from 'clsx'; // ... export default function NavLinks() { const pathname = usePathname(); return ( <> {links.map((link) => { const LinkIcon = link.icon; return ( <Link key={link.name} href={link.href} className={clsx( 'flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3', { 'bg-sky-100 text-blue-600': pathname === link.href, }, )} > <LinkIcon className="w-6" /> <p className="hidden md:block">{link.name}</p> </Link> ); })} </> ); } ``` ## fetching data 1. 使用 React Server Components (server 端接資料),則不需要 API layer 2. 如果在 client 端皆資料的話,則需要 [API layer](https://nextjs.org/learn/dashboard-app/fetching-data#api-layer) 在 server 端撈 sql 的資料步驟 /lib/data.ts ```typescript export async function fetchRevenue() { // Add noStore() here prevent the response from being cached. // This is equivalent to in fetch(..., {cache: 'no-store'}). try { // Artificially delay a response for demo purposes. // Don't do this in real life :) console.log('Fetching revenue data...'); await new Promise((resolve) => setTimeout(resolve, 3000)); const data = await sql<Revenue>`SELECT * FROM revenue`; console.log('Data fetch complete after 3 seconds.'); return data.rows; } catch (error) { console.error('Database Error:', error); throw new Error('Failed to fetch revenue data.'); } } ``` /dashboard/page.tsx ```typescript // 因為 Page 元件是非同步函示 所以可以等待資料來源 const revenue = await fetchRevenue(); ... <RevenueChart revenue={revenue} /> ``` 避免 request waterfalls ![image.png](https://hackmd.io/_uploads/Bk4pnM8X6.png) 可利用 Parallel data fetching 解決上述問題 提升效能 ```typescript export async function fetchCardData() { try { const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`; const customerCountPromise = sql`SELECT COUNT(*) FROM customers`; const invoiceStatusPromise = sql`SELECT SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid", SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending" FROM invoices`; const data = await Promise.all([ invoiceCountPromise, customerCountPromise, invoiceStatusPromise, ]); // ... } } ``` ## 骨架屏 1. 整頁骨架屏:搭配loading.tsx實作 /dashboard 頁面專屬的骨架屏 (必須放在(overview:檔名隨便)的資料夾中) ![image.png](https://hackmd.io/_uploads/ryB3QSIXT.png) 2. 元件骨架屏:為了避免 request waterfalls,將元件拆細,便可做到 code split 的效果,並且利用 react Suspense 元件做出骨架屏的效果 ```typescript <Suspense fallback={<LatestInvoicesSkeleton />}> <LatestInvoices /> </Suspense> ``` - You could stream the whole page like we did with loading.tsx... but that may lead to a longer loading time if one of the components has a slow data fetch. - You could stream every component individually... but that may lead to UI popping into the screen as it becomes ready. - You could also create a staggered effect by streaming page sections. But you'll need to create wrapper components. ## 利用 URL 實作搜尋功能 1. usePathname() => 取得全部網址 2. useSearchParams() => 取得 query 3. useRouter() => 切換搜尋列表網址並有 SPA 的效果 4. URLSearchParams => web API 5. use-debounce => debounce 套件 6. 利用上述 client 端取得 server 端的參數 去搜尋資料庫中的資料 [參考這頁 adding-search-and-pagination](https://nextjs.org/learn/dashboard-app/adding-search-and-pagination) ## CRUD 利用 React Server Actions 建立方法 createInvoice React Server Actions 會創建 post 請求的 api 節點,所以我們不需要手動創建 並且利用 zod 檢查表單型別(server端) - 利用react server actions 實作新建資料的邏輯 - 在頁面引入後 利用action的 props 觸發方法 `<form action={createInvoice}>` ## 處理錯誤 1. 使用 try catch 處理 server 端的錯誤 2. 使用 error 元件處理所有錯誤頁面(必須是 client component) 3. 使用 notFound(), not-found 元件處理401錯誤 ## 表單檢查 - require attr - server 端使用 zod 驗證表單 ## isAuth 檢查 1. 使用 next-auth 2. 被保護的路由只有在 middleware 驗證通過才會 render 3. bcrypt 套件依賴 Node.js (產生密碼用的) ## Meta Data ```htmlembedded <!--社群軟體分享時會出現的資訊--> <meta property="og:title" content="Title Here" /> <meta property="og:description" content="Description Here" /> <meta property="og:image" content="image_url_here" /> ``` ## 結論 優點: 1. 提供了許多好用的 api, component 包含路由、圖片優化等 2. 活用了 react server component 的用法 3. 用資料夾來決定路由的方式在小型專案的情況下非常方便 4. 這次新推出的官方課程算是簡短好上手,相當推薦嘗試看看 缺點: 1. 雖然狀態管理一樣推薦用 redux,但是 Server Components 是一种新的實驗性功能,需要重新考慮如何管理和共享狀態 2. 儘管也有管理元件的概念,但”資料夾決定路由“這件事情在大型專案下的維護性值得觀察