Johnny Wang BlogJohnny Wang Blog
Articles
Project
Live2d
johnnywang/book
Articles
Project
Live2d
johnnywang/book
  • About Me

    • 關於 Johnny
    • 文章清單
  • Javascript

    • View Transitions - 認識並使用原生 document 轉場效果
    • 用 Web Container 打造自己的線上 NodeJS 開發環境
    • 來試用看看原生 Web Popover API
    • 在電腦裡搞一個 RWKV AI 小助手
    • 遊戲 App 素材解包學習紀錄
    • 動手自己做一個 ChatGPT UI 工具吧
    • Create a React Server Components Project without NextJS - 製作一個不依賴 NextJS 的 React Server Components 專案
    • 如何在 Vuepress 裡快樂寫 React
    • 從 Mock Service Worker 源碼中學習
    • 擺脫 Node modules 地獄,擁抱 Yarn Plug'n'Play(PnP)
    • 快速上手 NextJS v13 - 基礎觀念 AppRouter 篇
      • 前言
      • 首先 NextJS v13 究竟做了啥?
      • 初始化 0 配置
      • Server Component VS Client Component
      • 什麼是 App Router?
      • Page 定義基本頁面
      • Layout 定義共享版型
      • Head(頭?)去哪了?
      • 連結、路由切換 - Linking and Navigating
      • 路由群組 - Route Group
      • 載入畫面 - Loading UI
      • Not Found 畫面
      • 錯誤處理 - Error Handling
      • 路由處理 - Route Handler
      • Server Action(v14.0.0 更新為預設支援)
      • 結論
    • 快速上手 NextJS v13 - Data Fetching, Caching, Revalidating 篇
    • 用 Socket.io 搭配 Matterjs 製作一款 Real-Time Canvas 聊天室(文長慎入)
    • 如何只用一支 CDN 及 4行設定,讓瀏覽器讀懂 Typescript, React, Vue
    • 用 tsup 快速建立 Typescript 開發環境
    • 開發 Email EDM 你可以更輕鬆
    • 關於我的 Side project - Maju Web Editor
    • Web URL 中神奇的 createObjectURL method
    • Awesome Vite 一個由社群維護的 template 集大成
    • Vue、React 使用心得分享(文長慎入)
    • 一起動手用 Socket.io 和 Peerjs 打造 WebRTC 即時視訊
    • Temporal Typescript SDK 學習筆記
    • React Web3 Storage
    • React useState 取得最新值
    • Babel 7 Decorator 的神奇小問題
    • 用 Koa + Vite + Pinia 打造基礎 SSR 環境
    • Live2d 官方範例改寫
    • 如何把 Video 畫在 Canvas 上
    • 史上最簡單的 Webpack 5 教學
    • 在瀏覽器中直接 import Vue SFC 開發起來
    • 手寫一個可中斷的 delay promise
    • NodeJS 輕量開發框架 Expressjs 與 Koa2 的區別
    • 用 2D 物理引擎 Matterjs 製作經典馬力歐 1-1
    • 原生 Javascript 的類型標註工具 JSDoc
    • Pinia - Vuex 的後繼者
    • 用 Nodejs 寫個 FTP command line 工具
    • Vue3 Server Render 手把手帶你搭建
    • 你真的懂 Event Loop 嗎
    • Babel7 基本介紹與使用
    • Vue/Vitejs 部分源碼解析
    • Vuejs 依賴追蹤 2020 版
    • Vuejs 依賴追蹤 2019 版
    • Js literal 模板編譯
    • 用 JavaScript 鎖定用戶調整畫面比例
    • Date.now() 與 new Date()
    • Promise 相關知識
    • Web Component 學習筆記
    • TypeScript 基礎篇
    • TypeScript 進階篇
    • MVC, MVVM, MVI 軟體設計架構
    • MVVM 簡單模擬框架實作
  • CSS & Sass

    • 2024 CSS 年度報告筆記
    • 2021 CSS 年度報告筆記
    • 如何不用 setTimeout 幫 display: none 的 DOM 加動畫
    • 純 CSS 實作星球環繞動畫效果
    • 差點錯過的 Tailwindcss 入門學習筆記
    • Tailwindcss 進階學習筆記
    • Sass, SCSS Built-In-Modules 內建模組與函數
    • SCSS Parent Selector
    • CSS 畫面鎖橫屏時,滾動的問題!!
    • Safari 使用 animation 時動態產生 rem 的坑
    • CSS: mix-blend-mode 屬性混合圖層動畫
  • Memo

    • Javascript

      • Yarn Plug'n'Play (PnP) 的 VSCode 設定方式
      • 如何用 Web API 讀取剪貼簿內容
      • CSP (Content Security Policy) 是什麼?
      • Astro 入門學習筆記
      • 如何解決 Astro 套用主題切換時,畫面抖動瞬閃問題
      • Rspack - 以 Rust 打造的快速構建工具
      • Typescript v5 Decorator 學習筆記
      • Sequelize 筆記
      • ES2022 學習筆記
      • Crypto 密碼加密方法
      • 實作 Test 的注意事項
      • Jest 測試工具 - 基礎篇
      • 正則表達式
      • Rxjs 學習筆記
      • Gulp 學習筆記
      • Clean Code Javascript 學習
      • JS 實戰開發特殊小技巧
    • React

      • React forwardRef 使用筆記
      • 如何在 Nextjs 中使用 middleware set cookie
      • React Styled-Components 基礎篇
      • React Styled-Components 進階篇
      • React Unit Test
      • React Testing Library
      • React Emotion Basic
      • React Styled-JSX
      • React 18 - Concurrent Features Memo
      • useEffect 的高階封裝範例
      • React useContext & useReducer 搭配
      • React Router Config 筆記
    • Vue

      • Cypress Vue
      • Vue 單元測試學習筆記
      • 學習 Vuetify 的一些筆記
      • Vuex 學習筆記
    • React Native

      • React Native - basic
      • Expo Basic
      • Expo Router
      • Expo Build
      • Issues
    • GraphQL

      • GraphQL 學習筆記 - 基礎篇
      • GraphQL 學習筆記 - 進階篇
      • Apollo Client 學習使用筆記
      • GraphQL Memo
    • Parse

      • Parse Javascript 文檔閱讀筆記
      • Parse User Object 章節
      • Parse Session Object 章節
      • Parse Schema Object 章節
      • Parse Cloud Code 章節
    • CSS

      • Sass, SCSS 基礎筆記
    • Docker

      • Docker 基礎技術
      • Dockerfile 技術篇
      • Docker-compose 技術篇 - 3.7
      • Kubernetes 學習筆記
      • ArgoCD 學習筆記
      • Podman 學習筆記
      • Colima 安裝使用筆記
    • Git Learning

      • Git 版本控制
      • Git 基礎使用 - init, clone, add, commit, status, log
      • Git 分支
      • Git Merge 分支合併指令
      • Git Stash 保存紀錄
      • Git 基礎復原指令 - restore
      • Remote 遠端協同工作 - remote, fetch, pull, push
      • Git Tag 標籤 - tag
      • Git 設定 - config
      • Git 檔案比對與差異 - diff
      • Git Rebase 定義分支的參考基準
      • Subtree 子樹
      • Git Reflog 指令紀錄
      • Git Filter Branch
      • Git Crypt 使用筆記
      • Git 客製化工具
    • Bash

      • Bash 基礎概念
      • Variable 變數
      • Script 腳本撰寫
      • Condition 流程控制
      • Operation 算數表達式
      • Function 函數
      • Command Line Params 指令列參數處理
    • Patterns

      • Introduce
      • Design Patterns

        • Singleton Pattern
        • Compound Pattern
        • Proxy Pattern
        • Hooks Pattern
        • Observer Pattern
        • Mediator/Middleware Pattern
        • HOC Pattern
        • Factory Pattern
        • Render Props Pattern
        • Flyweight Pattern
        • Container/Presentational Component
        • Prototype Pattern
        • Mixin Pattern
        • Provider Component
        • Command Component
        • Module Pattern
      • Render Patterns

        • Rendering Patterns 渲染模式介紹
    • FE 性能優化

      • 前端性能優化
      • 靜態資源優化
      • 頁面渲染架構優化
      • Server 與 Network 優化
      • 開發流程、監控體系優化
    • Ollama 筆記
    • Nginx JS module 基本使用筆記
    • FlowiseAI 使用介紹筆記
    • Github Copilot 使用筆記
    • Traefik Memo
    • API First 學習筆記
    • What is AC - Acceptance Criteria 驗收條件
    • Mermaid 學習筆記
    • 串接 Sonarcube 筆記
    • Youtube Data API 基礎使用筆記
    • FB API 學習筆記
    • VSCode 好用快捷鍵
    • 在 Simulator 上開發測試
    • 問題修正紀錄
  • References

    • 全端開發學習資源
    • 實用工具
    • 網頁前端課程目錄
  • Daily

    • 中醫學習

      • 中醫自學方向
      • 黃帝內經-素問
      • 基礎理論

        • 中藥治病
        • 中藥產地與採收
        • 陰陽五行
        • 精氣血津液
        • 整體觀與臟象
        • 病因病機
        • 辨證論治
        • 四診八綱
        • 五運六氣
        • 經絡
      • 診斷學

        • 辯證
        • 望診
        • 聞診
        • 問診
        • 脈診簡史
        • 切診
        • 常見脈象與臨床意義
        • 經脈辨證
        • 中醫的六邪在疾病初期、後期分別有那些症狀
      • 中藥學

        • 中藥基礎理論
        • 常見中藥材
        • 解表藥(30)
        • 清熱藥(82)
        • 瀉下藥(16)
        • 利水渗湿药(8)
        • 化痰止咳平喘药(15)
        • 止血藥(8)
        • 開竅-平肝-安神藥(11)
        • 化濕藥(5)
        • 祛風濕藥(12)
        • 理氣藥(13)
        • 活血祛瘀藥(7)
        • 消食藥(4)
        • 驅蟲藥(6)
        • 祛寒药(6)
        • 補虛藥(43)
        • 收斂藥(13)
      • 方劑學

        • 總論
        • 解表劑
        • 瀉下劑
        • 清熱劑
        • 理血劑
        • 去暑劑
        • 去濕劑
      • 針灸學

        • 特定穴
      • 其他

        • 內證觀察筆記 上篇【內證漫談】
        • 內證觀察筆記 中篇【太極器官、五藏】
        • 內證觀察筆記 下篇【十二正經】
        • 磁力對生理的影響
    • 易經學習

      • 易經入門
    • 道德經學習

      • 道德經學習重點筆記
      • 第一章 道可道,非常道
      • 第二章 夫唯弗居,是以不去
      • 第三章 不尚賢,使民不爭
      • 第四章 道沖而用之或不盈,淵兮似萬物之宗
      • 第五章 天地不仁,以萬物為芻狗,多聞數窮,不如守中
      • 第六章 谷神不死,是謂玄牝
      • 第七章 天長地久。天地所以能長且久者,以其不自生,故能長生。
      • 第八章 上善若水。水善利萬物而不爭,處眾人之所惡,故幾於道
      • 第九章 功成身退,天之道也。
      • 第十章 載營魄抱一,能無離乎﹖
      • 第十一章 有之以為利,無之以為用
      • 第十二章 五色令人目盲,五音令人耳聾,五味令人口爽,馳騁畋獵令人心發狂,難得之貨令人行妨。
      • 第十三章 寵辱若驚,貴大患若身。愛以身為天下,若可托天下
      • 第十四章 是謂無狀之狀,無物之象,是謂惚恍。迎之不見其首,隨之不見其後。執古之道,以御今之有。
      • 第十五章 保此道者,不欲盈。夫唯不盈,故能蔽而新成。
      • 第十六章 致虛極,守靜篤。萬物並作,吾以觀復。
      • 第十七章 太上,下知有之。悠兮其貴言,功成事遂,百姓皆謂我自然。
      • 第十八章 大道廢,有仁義;智慧出,有大偽;
      • 第十九章 絕聖棄智,民利百倍。見素抱樸,少私寡欲
      • 第二十章 絕學無憂。眾人皆有餘,而我獨若遺。
      • 第二十一章 道之為物,惟恍惟惚。惚兮恍兮,其中有象;恍兮惚兮,其中有物。
      • 第二十二章 曲則全,枉則直,窪則盈,敝則新,少則得,多則惑。是以聖人抱一為天下式
      • 第二十三章 希言自然。天地尚不能久,而況於人乎?信不足焉,有不信焉。
      • 第二十四章 企者不立,跨者不行
      • 第二十五章 有物混成,先天地生。人法地,地法天,天法道,道法自然。
      • 第二十六章 重為輕根,靜為躁君。輕則失本,躁則失君。
      • 第二十七章 善行無轍跡,善言無瑕謫。故善人者,不善人之師;不善人者,善人之資。不貴其師,不愛其資,雖智大迷,是謂要妙。
      • 第二十八章 知雄守雌,為天下谿,知榮守辱,為天下谷
      • 第二十九章 天下神器,不可為也,是以聖人去甚,去奢,去泰。
      • 第三十章 以道佐人主者,不以兵強天下
      • 第三十一章 夫兵者,不祥之器,物或惡之,故有道者不處。
      • 第三十二章 道常無名,樸雖小,天下莫能臣也。
      • 第三十三章 知人者智,自知者明。
      • 第三十四章 大道氾兮,其可左右。以其終不自為大,故能成其大。
      • 第三十五章 執大象,天下往。往而不害,安平太。樂與餌,過客止。
      • 第三十六章 將欲歙之,必固張之;將欲弱之,必固強之
      • 第三十七章 道常無為而無不為
      • 第三十八章 上德不德,是以有德;下德不失德,是以無德
      • 第三十九章 昔之得一者,天得一以清。不欲琭琭如玉,珞珞如石。
      • 第四十章 反者道之動,弱者道之用。天下萬物生於有,有生於無
      • 第四十一章 上士聞道,勤而行之;大方無隅,大器晚成,大音希聲,大象無形,道隱無名。
      • 第四十二章 道生一,一生二,二生三,三生萬物。
      • 第四十三章 天下之至柔,馳騁天下之至堅
      • 第四十四章 名與身孰親﹖身與貨孰多﹖得與亡孰病﹖
      • 第四十五章 大成若缺,其用不弊。大盈若沖,其用不窮。
      • 第四十六章 天下有道,卻走馬以糞。天下無道,戎馬生於郊。
      • 第四十七章 不出戶,知天下;不窺牖,見天道。
      • 第四十八章 為學日益,為道日損。損之又損,以至於無為。
      • 第四十九章 聖人無常心,以百姓心為心。
      • 第五十章 出生入死。生之徒,十有三;死之徒,十有三;人之生,動之死地,亦十有三。
      • 第五十一章 道生之,德畜之,物形之,勢成之。
      • 第五十二章 天下有始,以為天下母。以知其子,既知其子,復守其母,沒身不殆。
      • 第五十三章 使我介然有知,行於大道,唯施是畏。大道甚夷,而人好徑。
      • 第五十四章 善建者不拔,善抱者不脫,子孫以祭祀不輟。
      • 第五十五章 含德之厚,比於赤子。知和曰常,知常曰明。益生曰祥。心使氣曰強。
      • 第五十六章 知者不言,言者不知。
      • 第五十七章 以正治國,以奇用兵,以無事取天下。
      • 第五十八章 其政悶悶,其民淳淳;其政察察,其民缺缺。禍兮福之所倚,福兮禍之所伏。
      • 第五十九章 治人事天,莫若嗇。夫唯嗇,是謂早服
      • 第六十章 治大國,若烹小鮮。夫兩不相傷,故德交歸焉。
      • 第六十一章 大國者下流,天下之交。天下之牝,牝常以靜勝牡,以靜為下。
      • 第六十二章 道者萬物之奧。雖有拱璧以先駟馬,不如坐進此道。
      • 第六十三章 為無為,事無事,味無味。圖難於其易,為大於其細;
      • 第六十四章 合抱之木,生於毫末;九層之臺,起於累土;千里之行,始於足下。為者敗之,執者失之。
      • 第六十五章 古之善為道者,非以明民,將以愚之。
      • 第六十六章 是以欲上民,必以言下之。欲先民,必以身後之。
      • 第六十七章 天下皆謂我道大,似不肖。夫唯大,故似不肖。
      • 第六十八章 善為士者不武,善戰者不怒,善勝敵者不與
      • 第六十九章 吾不敢為主而為客,不敢進寸而退尺。禍莫大於輕敵,輕敵幾喪吾寶。
      • 第七十章 吾言甚易知,甚易行。天下莫能知,莫能行。
      • 第七十一章 知不知上,不知知病。夫唯病病,是以不病。
      • 第七十二章 民不畏威,則大威至。無狎其所居,無厭其所生。夫唯不厭,是以不厭。
      • 第七十三章 天之道,不爭而善勝,不言而善應,不召而自來,繟然而善謀。
      • 第七十四章 民不畏死,奈何以死懼之?若使民常畏死,而為奇者,吾得執而殺之,孰敢?
      • 第七十五章 民之饑,以其上食稅之多,是以饑。
      • 第七十六章 人之生也柔弱,其死也堅強。萬物草木之生也柔脆,其死也枯槁。
      • 第七十七章 天之道,其猶張弓與﹖高者抑之,下者舉之;有餘者損之,不足者補之。
      • 第七十八章 天下莫柔弱於水,而攻堅強者莫之能勝,以其無以易之。
      • 第七十九章 和大怨,必有餘怨,安可以為善﹖
      • 第八十章 小國寡民。使有什伯之器而不用,使民重死而不遠徙。
      • 第八十一章 信言不美,美言不信。天之道,利而不害;聖人之道,為而不爭。
    • 人類真相推廣協會

      • 人類真相介紹
      • 人生大挑戰-閱讀筆記
      • 書籍-人生大挑戰

        • 序文
        • 童年的回憶
        • 賺外快的童年
        • 養家的童年
        • 少年時期的回憶
        • 粉紅睡衣女鬼的祕密
        • 麵線、甘蔗和賽鴿
        • 我在黑社會的日子
        • 『台北一條龍』
        • 賭徒‧妻子‧盤仔人
        • 人鬼之戰—正邪不分的恐怖
        • 開展創業石銅雕畫的日子
        • 渡畜牲者‧瞎掰鬼與邪靈
        • 無所不在的陷阱—邪靈的詭計
        • 鬼屋‧符令‧大揭祕
        • 妖魔鬼怪大變身—邪靈與動物
        • 乩童與宮廟的祕密
        • 活鬼纏身的恐怖亂象
        • 前面親兄弟、後面無情義的假面
        • 前面手牽手、後面下毒手的險境
        • 人心險惡、五馬分屍所逼自殺的真相
        • 自殺後的奇遇—人死後的世界
        • 我自殺後的奇遇—「陰間地府處」與「陰府大本營」
        • 我被趕出家門、面臨眾叛親離的苦境
        • 其他
    • 英文學習

      • 自我介紹
      • 簡單寒暄
      • 指路
      • 買賣
      • 轉接電話
      • 通勤
      • 訂位
      • 數字
    • 金剛經 時間不存在?自我是虛擬?
    • 20250319 外婆過世
    • 說服的藝術:避免陷阱,建立共識
    • 禪修一定要去除「如何」嗎?
    • 不對稱的回報
    • 避免慢性壓力
    • 關鍵溝通表達力
    • 快樂的秘訣
    • 生活在現代社會,維持高品質思考的重要性
    • 心理學家的面相術:解讀情緒的密碼
    • 什麼是真相,真相是什麼
    • 身為一位歷史觀察者(文長慎入)
    • 2022 Leisure Learn
    • 兩年八個月,我從 Garena 畢業了
    • DeFi
    • 我的終生大事,交往到結婚
    • 轉職前端工程師 3 年多的回顧
    • MacOS + Iterm2 + Oh My Zsh
    • 我喜歡的名言佳句
    • Web Interview Preparation

快速上手 NextJS v13 - 基礎觀念 AppRouter 篇

Share On:

FacebookLINEMessengerTelegram

前言

Hi 大家好,我是 Johnny,這陣子公司同事開始接觸到 Next13 相關的開發,覺得我也是時候來研究下,索性花了幾個小時快速體驗了一下,並將一些我覺得比較重要的特點記錄下來,這篇是我速讀 Next13 官方文件後整理的一個隨性筆記!主要包含 基礎觀念, AppRouter 的規則等等,Data Fetching 會再出一篇介紹,本篇主要專注在 Next13 的新功能、觀念上面。

筆者撰寫文章時 NextJS 版本為 v13.4.4

首先 NextJS v13 究竟做了啥?

先簡單總結幾個最大的特點

  • 初始化 0 配置,讓新手也能專注在理解 NextJS 的資料夾結構、概念,而不是浪費時間在一堆非必要的初始設定、配置
  • 新的 App Router 模式,提供高度客製化 Routing 的能力
  • Server Component 模式及相關好用 API,提供開發前端程式時的資料處理安全性、SEO 優化能力、以及盡可能地減少 client side 消耗的處理效能
  • Server-Centric routing 快速反應路由切換,Router 內建 in-memory client-side cache 機制,避免不必要的頁面內容 reload 影響效能

以上是我目前對於 NextJS v13 的一點理解,下面就來實際看看程式碼吧!

初始化 0 配置

透過 yarn create next-app 快速建立新的 NextApp,點開 next.config.js 只有下面這樣,對於新用戶來說可以完全忽略這個 config 設定部分!雖然後期開發一定會需要調整,但在初期建立專案或新手來說非常方便。

/** @type {import('next').NextConfig} */
const nextConfig = {}

module.exports = nextConfig

Server Component VS Client Component

預設為 Server Component,可透過在檔案最上方定義 use client, use server 明確設定

// 預設其實就是 use server,除了 error handling component
'use server';

import Image from 'next/image'

export default function Home() {
  return (<div>Hello Home</div>);
}

如果在 server component 中使用像是 useEffect, useState 等 client side only 的 hook 將會報錯

ReactServerComponentsError:
You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.

想要像原本 Pages Router 方式撰寫 client side component 可以這樣寫

'use client';

import Image from 'next/image';

// 下面就是 Pages Router 方式的原本寫法,這裡省略 =V=

When to use Server and Client Components?

具體何時該使用 server component, client component 可參考官方網站

底下是一個根據官網資訊製作的簡單對照表格,僅供參考

What do you need to do?Server ComponentClient Component
Fetch data✅❌
Access backend resources (directly)✅❌
Keep sensitive information on the server (access tokens, API keys, etc)✅❌
Keep large dependencies on the server / Reduce client-side JavaScript✅❌
Add interactivity and event listeners (onClick(), onChange(), etc)❌✅
Use State and Lifecycle Effects (useState(), useReducer(), useEffect(), etc)❌✅
Use browser-only APIs❌✅
Use custom hooks that depend on state, effects, or browser-only APIs❌✅
Use React Class components❌✅

什麼是 App Router?

App Router 是 v13 新介紹的一種 routing 方式,與之相對過去的方式被稱作 Pages Router,可以在官方網站左側按鈕切換兩種路由模式的 Documentation

為求說明方便,底下內容皆以 Typescript 進行說明,App Router簡稱A.R,Pages Router簡稱P.R

首先了解一下一個概念,比較好理解後面提到的東西:

  • P.R: 以 File 為單位定義頁面,該頁面相關設定必須放在外部管理、引入,較難客製化
  • A.R: 以 Folder 為單位定義頁面,該頁面所有相關設定可直接放在 folder 層級中,可高度客製化

app 資料夾裡都裝什麼?

A.R 方式在 app 資料夾中主要可以使用以下幾種 NextJS 會進行處理的文件

  • page.tsx: 定義當前層級頁面(與 route.tsx 不可同時存在同一層中)
    • route.tsx: 定義當前層級 route handler(類似P.R方式時的pages/api/xxx.tsx)
  • layout.tsx: 定義當前層級與子層共享的 UI 版型(若父層有 layout,會被父層 layout 包裹起來)
    • template.tsx: 與 layout 類似,但在路由切換時會 remount 新的 instance(layout 若路由切換前後一樣,則相同部分不會被 remount 新 instance)
  • loading.tsx: 定義當前層級頁面與子層的 loading UI
  • error.tsx: 定義當前層級頁面與子層的 error UI(不會 catch 同一層的 layout.tsx 錯誤,需要 catch layout.tsx 錯誤需要在上一層級的 error.tsx 中處理,若為 rootLayout,則請在 global-error.tsx 中處理)
    • global-error.tsx: 與 error.tsx 基本相同,但主要用來定義處理 rootLayout 的錯誤
  • not-found.tsx: 定義當 component 中呼叫 notFound 方法或是匹配不到任何頁面時的 UI

以上檔案在各層中會反覆出現,並不是只有一層喔!~除了以上基本的檔案名稱外,可以參考這邊查看其他會被 NextJS 處理的檔案名稱,除了這些檔案名稱外的檔案可以安心放在裡面自由運用

Page 定義基本頁面

以下兩種模式分別建立路徑 /, /about 的頁面,Dynamic Routing 機制可參考這邊

  • P.R: pages/index.tsx, pages/about.tsx
  • A.R: app/page.tsx, app/about/page.tsx
// `app/page.tsx` is the UI for the `/` URL
export default function Page() {
  return <h1>Hello, Home page!</h1>;
}

下圖是 Nested Routing 範例

Page 的特點

  • page 永遠是路由 Tree 的最末端葉片部分
  • 如果要讓該層路由可被 public 訪問,必須建立 page
  • page 預設為 Server Component,但可以手動設定為 Client Component
  • page 可以進行 Fetch Data 操作,詳情請見這裡

Layout 定義共享版型

以下兩種模式分別建立路徑 /, /about 頁面的 layout,其中 / 的 layout 會將 /about 的 layout 包裹在其中

  • P.R: 沒有內建,需手動處理完成需求
  • A.R: app/layout.tsx, app/about/layout.tsx

Layout 必須明確接收 prop children,並將其返回

export default function DashboardLayout({
  children, // will be a page or nested layout
}: {
  children: React.ReactNode;
}) {
  return (
    <section>
      <nav></nav>

      {children}
    </section>
  );
}

下圖是 Nested Layout 範例

Layout 的特點

  • app 資料夾中,最上層的 layout.tsx 被稱為 RootLayout
  • 路由中的 layout 可以 nested,透過 children 層層包裹
  • 透過 Route Groups 可以選擇性讓路由套用不同的 layout
  • layout 預設為 Server Component,除了 RootLayout 外,可以手動設為 Client Component

關於 RootLayout

基本上是用來取代 P.R 方式中的 _app, _document

  • 必須存在且為 Server Component,將套用到整個應用程式頁面當中
  • 必須在其中包含 <html> and <body> tag,因為 NextJS 並沒有為用戶定義
  • 透過 Route Groups 可以建立 multiple root layout

Route(Page, Layout, Route Handler) 可以設定 Route Segment Config Option 對路由進行設定調整

Head(頭?)去哪了?

過去我們常用 <Head> component 幫助我們在頁面中客製化 SEO meta,現在我們可以直接透過 metadata object 或 generateMetadata function 定義

import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Next.js',
};

export default function Page() {
  return '...';
}

官方良心建議:盡量使用 Metadata API,不要手動在 RootLayout 使用 <head> 加入 <title>, <meta>等,前者會自動處理套用 streaming, de-duplicating <head> 標籤的功能

連結、路由切換 - Linking and Navigating

Link

連結切換同樣使用 NextJS 提供的 <Link> 就行~詳細文件可以參考這邊

Navigating 流程機制

  • route transition 被 <Link> or router.push() 觸發
  • router 更新 browser 網址列 URL
  • router 透過重複利用未改變的 client-side cache(e.g. shared layouts) 避免不必要的更新工作,也稱作 partial rendering
  • 如果符合 soft navigate 條件,則直接從 cache 返回內容,不會再去 server 拿一次,不符合則進行 hard navigate 從 server 獲取 Server Component payload
  • created 後,當從 server 獲取 payload 時顯示 Loading UI
  • router 將 cache 或更新的 payload 渲染在 client

關於 client-side cache 的詳細解釋請看這裡

關於 soft/hard navigate 的詳細解釋請看這裡

路由群組 - Route Group

簡單來說,route group 就是一種讓我們在 A.R 方式的 app 資料夾下,將 routes 分門別類拆分開來,卻又不影響原來 route 的解析流程的一種技術

啥意思勒?看圖看圖~

透過 (group-name) 的方式,我們將屬於同一類型的頁面集中放在其中,命名為 (marketing),透過這方式我們可以很輕易的把 route 進行分類管理,大幅提升開發體驗

同層級 Multiple Layout

另一個 route group 的好用之處在於,我們可以在不同 group 中定義只屬於該 group 的 layout,且不影響其外部的其他頁面

Multiple Root Layout

如果把最上層的 app/layout.tsx 刪掉,並在其中的 group 中各自定義 layout.tsx,就可以讓同一應用程式套用完全兩套獨立的 Root Layout,對於需要在同一系統中顯示完全不互相影響的 layout 進行開發非常有幫助

Route Group 的特點

  • group 的命名完全不影響 route 解析
  • 請勿在 group 中重複使用相同的 URL path,將會報錯((marketing)/about/page.js, (shop)/about/page.js 兩者會衝突)
  • 在 multiple root layouts 頁面間切換路由將觸發 full page load 整頁刷新(相對於原本的 client-side load)

載入畫面 - Loading UI

loading.tsx 基本上是以下圖結構的方式,將頁面內容包裹在 Suspense 當中,詳細可參考這裡

export default function Loading() {
  // You can add any UI inside Loading, including a Skeleton.
  return <LoadingSkeleton />;
}

Not Found 畫面

not-found.tsx 的簡單範例如下:

/* app/blog/not-found.tsx */
import Link from 'next/link'

export default function NotFound() {
  return (
    <div>
      <h2>Not Found</h2>
      <p>Could not find requested resource</p>
      <Link href="/">Return Home</Link>
    </div>
  )
}

注意點:root 的 not-found.tsx (app/not-found.tsx) 除了處理已知的 notFound error之外,也會一併匹配處理所有未知路徑錯誤

not-found 作為一個 server component 同樣能進行 data fetching

import Link from 'next/link'
import { headers } from 'next/headers'

export default async function NotFound() {
  const headersList = headers()
  const domain = headersList.get('host')
  const data = await getSiteData(domain)
  return (
    <div>
      <h2>Not Found: {data.name}</h2>
      <p>Could not find requested resource</p>
      <p>
        View <Link href="/blog">all posts</Link>
      </p>
    </div>
  )
}

錯誤處理 - Error Handling

error.tsx 基本上是以下圖結構的方式,將頁面內容包裹在 ErrorBoundary 當中,詳細可參考這裡

// Error components 必須為 Client Components
'use client';

import { useEffect } from 'react';

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  useEffect(() => {
    // Log the error to an error reporting service
    console.error(error);
  }, [error]);

  return (
    // 嘗試透過 re-render 恢復 segment
    <div>
      <h2>Something went wrong!</h2>
      <button onClick={() => reset()}>
        Try again
      </button>
    </div>
  );
}

錯誤恢復機制 - Recovering From Errors

error.tsx 提供一套錯誤恢復機制,透過呼叫 reset function,可以讓 page 重新進行嘗試載入,詳細可參考這裡

注意:error.tsx 並不 catch 同層 layout 中的 error,若要 catch layout 請在上一層中的 error.tsx 或是 root 的 global-error.tsx處理。

'use client';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <html>
      <body>
        <h2>Something went wrong!</h2>
        <button onClick={() => reset()}>Try again</button>
      </body>
    </html>
  );
}

路由處理 - Route Handler

route.tsx 讓開發者能透過 web api request, response 對請求進行處理(API...),與 layout.tsx一樣可以在 app 的任意子層中出現,但不可與 page.tsx 出現在同一層中,將會報錯

// app/my-api/route.tsx
import { NextResponse } from 'next/server';

export const GET = async (equest: Request) => {
  return NextResponse.json({
    msg: 'Johnny Good Good',
  })
}

Behaviors

  • 靜態路由處理:預設 GET 為靜態處理
  • 動態路由處理:符合以下條件將為動態處理
    • 在 GET 中使用到 Request object
    • 使用到任何 GET 以外的 HTTP methods
    • 使用到任何 dynamic function,比如 cookies, headers,詳細可參考這裡
    • 手動設定 Segment Config Options 為 dynamic mode
// app/my-api/products/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get('id');
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  });
  const product = await res.json();

  return NextResponse.json({ product });
}

Share On:

FacebookLINEMessengerTelegram

Server Action(v14.0.0 更新為預設支援)

在 v13 時 Server Action 處於 experiment 狀態,到了 v14 後移除了 experiment 開關,可以直接在 v14 中使用了

可以在兩個地方定義 Server Action

  1. 在需要使用的 Server Component 中直接定義並使用(無法在 Client Component 中定義)
  2. 在獨立的檔案中定義,並在 Client/Server Component 中引入使用
  • Server Component 使用 定義一個 async function,並在其中標記 use server,確保該 function 只在 server 環境被調用
// app/page.js
export default function ServerComponent() {
  async function myAction() {
    'use server'
    // ...
  }
}
  • Client Component 使用 在 client 中有 import, props 兩種方式可以使用 Import 在獨立檔案開頭定義 use server,則在該檔案中 export 的 function 都會被視為 Server Action,也因此可以在一個檔案中定義多個 Server Action
// app/actions.js
'use server'

export async function myAction() {
  // ...
}

接著在 Client Component 中引入使用即可

// app/my-client-component.js
'use client'

import { myAction } from './actions'

export default function ClientComponent() {
  return (
    <form action={myAction}>
      <button type="submit">Add to Cart</button>
    </form>
  )
}

Props 也可以把 Server Action 作為 props 傳遞給 client component 使用

<ClientComponent updateItem={updateItem} />
'use client'

export default function ClientComponent({ myAction }) {
  return (
    <form action={myAction}>
      <input type="text" name="name" />
      <button type="submit">Update Item</button>
    </form>
  )
}

Binding Arguments

可以透過 bind 把參數掛到 Server Action 上,提升靈活性,Client、Server Component 都可以這樣操作

'use client'

import { updateUser } from './actions'

export function UserProfile({ userId }) {
  // 綁定 userId 到 updateUser 參數中
  const updateUserWithId = updateUser.bind(null, userId)
  return (
    <form action={updateUserWithId}>
      <input type="text" name="name" />
      <button type="submit">Update User Name</button>
    </form>
  )
}

這樣在我們的 Server Action 中就可以額外拿到 userId

'use server'

export async function updateUser(userId, formData) {
  // ...
}

Invocation 調用觸發時機

可以在以下場景調用 Server Action

  • 使用 form action, formAction
  • 使用 startTransition,這個方式會 disable Progressive Enhancement

Progressive Enhancement

Server Action 的 Progressive Enhancement 機制能夠讓 <form> 元素在具備 Javascript 執行環境下依然能夠正常運作

Size Limit

預設情況下,Server Action 可傳遞的 request body 為 1MB,如果需要調整可透過如下方式在 next.config.js 進行修改

module.exports = {
  experimental: {
    serverActions: {
      bodySizeLimit: '2mb',
    },
  },
}

結論

這篇不知不覺又打了有點長,但這次 v13 真的很多新觀念,篇幅上稍微變得很長還請大家諒解,不過其實這樣還沒講完 XD,還有 Data Fetching 的部分就留待下一篇紀錄拉~

對我來說,對於 v13 的第一印象是「這是啥!?」,沒錯XD,相信有寫過 NextJS 一段時間的讀者應該都是差不多這感覺,不過細細看完 Documentation 後發覺,確實這次 v13 很多改版的內容解決了許多之前遇到的痛點,比如 layout 客製化彈性、server side 與 client side 互動模式、loading UI 套用等等在之前的版本都需要相當的精力去自己實作,雖然也很好玩能學到東西,但有些地方確實有重複造輪子的感覺,v13 更多的是在架構層面上引入了 Server Component 的理念,並將其真正融入到了原有的開發體驗中,相信之後 Next 的團隊還會再推出更多讓人耳目一新的概念與想法,推動社群繼續成長!

今天就先記錄到這邊拉,如果覺得文章對你有幫助的話,歡迎幫我分享給更多人看看喔~謝謝大家 =V=~~ 掰掰~

最近更新:: 2023/10/28 上午8:44
Contributors: johnnywang1994, johnnywang
Prev
擺脫 Node modules 地獄,擁抱 Yarn Plug'n'Play(PnP)
Next
快速上手 NextJS v13 - Data Fetching, Caching, Revalidating 篇