前面我們已經陸續看了很多天關於 Next.js Router 的用法,接下來幾天我們會來看一些使用 App Router 時,才可以使用的 Router 進階用法。今天先來看一個好懂又好用,同時能讓自己的專案更有架構地進行路由管理的 Route Groups 和 Private Folders。
在正式開始這個主題之前,我們先來思考一下,如果今天有一個龐大的專案,裡面同時存在著這幾種狀況:
在這個狀況下,在 Next.js 中,你會如何專案的資料夾結構及路由呢?
在 Page Router 的模式下,我們可能會這樣設計資料夾的結構
my-project/
└── 📁 src/
├── 📁 components/ # 放共用 UI 元件,或是某些頁面被拆出來的元件 (寫在 src 底下,不會變成路由)
│ ├── Header.tsx
│ ├── Footer.tsx
│ └── Sidebar.tsx
│ └── 📁 login/
│ │ └── LoginForm.tsx
│ └── 📁 register/
│ │ └── RegisterForm.tsx
│ └── 📁 userProfile/
│ │ └── ProfileCard.tsx
│ └── 📁 myFavoriteList/
│ └── ListTable.tsx
└── 📁 pages/ # Next.js 頁面資料夾,會自動對應路由
├── layout.tsx # Root layout(放 <html>、<body>、Header 等)
├── index.tsx # 首頁,不需要登入的頁面
├── 📁 login/ # 不需要登入的頁面
│ └── index.tsx
├── 📁 register/ # 不需要登入的頁面
│ └── index.tsx
├── 📁 user-profile/ # 需要登入才能進的頁面
│ └── index.tsx
└── 📁 my-favorite-list/ # 需要登入才能進的頁面
└── index.tsx
上述這樣的結構沒什麼問題,但我們可以嘗試從 易於管理
和 易於維護
的角度來思考以下這兩個問題:
我想答案很清楚,如果以前面提到的結構來建立專案,當專案內容越來越龐大,也就越難越區分某個路由的功用,以及查找路由中使用的元件。
為了解決這樣的問題,App Router 增加了兩個與 Page Router 不同的資料夾命名方式,也就是 Route Groups 和 Private Folders,幫助我們更好地組織專案與管理路由。
「Route Groups」是 Next.js App Router 中新增的一種資料夾命名方式,用來建立群組路由,也就是把路由做分類管理。Route Groups 的使用的方式是將資料夾名稱加上括號 ()
,例如我們可以用 (withFooter)
來去命名資料夾。
在創建資料夾時,若沒有特別在命名上加上 ()
,Next.js 會將其視為一個路由段來建立路由,也就是說當你用 login
這個名稱建立資料夾,且在底下建立 page 檔案時,當專案跑起,輸入 login 這個 path 在網址上時,會指向對應的頁面來顯示在畫面上。但是加上 ()
變成 (login)
時,並不會將 login 產生成實際的路由路徑,當你在網址上輸入 login 時,也就不會有對應的頁面。
這個特性在專案龐大,需要將路由分類的時候,非常有幫助。例如我們可以建立 (private)
和 (public)
兩個資料夾,用來明確區分來需要登入才能進入和不需要登入就能進入的路由。
如果有某些頁面需要共用一個 layout,但路由路徑卻不相同時,也適合透過 Router Groups 的方式來處理。例如:假設有五個頁面需要有 footer,有三個頁面不需要 footer,那就可以建立一個有 footer 的 layout 放在 (withFooter)
這個資料夾底下,再進一步依照要使用的路由路徑建立資料夾名稱。
my-project/
└── 📁 src/
└── 📁 app/
├── 📁 (withFooter)/ # (withFooter) 底下的頁面都會套用到這裡設定的 layout
│ ├── 📁 detail/
└── └── layout.tsx
除了 layout 外,如果被分類成同一個群組的路由有需要共用 error 或是 loading,也可以設定在指定的群組資料夾底下,讓下面的路由一起共用。
「Private Folders」是 Next.js App Router 中的另一個新增的資料夾命名方式,主要用途是用來建立不對外開放的資料夾。這裡的「不對外開放」,指的是不會產生一個對外的路由,即使在資料夾中設定了 page 檔案,也無法直接連進來。
要建立 Private Folders,可以用加上_
的名稱來命名資料夾,例如 _components
。這樣命名的資料夾,即使是建立於 app 資料夾底下,也不會產出一個對應的路由,但在這個資料夾中的內容,可以供其他頁面使用。由於 Private Folders 帶有這樣的特性,所以很適合被用在 App 資料夾中定義的一般路由資料夾底下,作為這個頁面會使用到的元件資料夾,或是共用邏輯的資料夾。
當然如果希望一些元件或是共用邏輯需要額外被拆分和整理起來,也可以將相對應的資料夾建立於 src 資料夾底下,但是如果不是全域性共用的元件或邏輯,使用 Private Folders 就近管理於 app 資料夾的指定路由資料夾底下,會更清楚明瞭地知道那些元件或邏輯的使用範圍,在維護上也會更便利。例如:login 頁面中才會使用到的元件就可以整理到 src/app/login/_components
底下。
my-project/
└── 📁 src/
└── 📁 app/
├── 📁 login/
│ ├──📁 _components/
│ │ ├── LoginForm.tsx # /login 頁面使用的元件,不會被建立成路由
│ └── page.tsx
這邊必須補充一點,如果某個元件、邏輯或是 custom hooks 在其他頁面上也會使用到,建議把相關的內容額外放到 src 下管理,比較不會誤會是專屬於這個路由頁面要使用的內容。
前面已經了解什麼是 Route Groups 和 Private Folders 了,我們再回頭用 Route Groups 和 Private Folders 改寫一下前面提到過的路由設定。
my-project/
└── 📁 src/
├── 📁 components/ # 全域共用元件(真的是全域共用才放這)
│ ├── Header.tsx
│ ├── Footer.tsx
│ └── Sidebar.tsx
└── 📁 app/
├── layout.tsx # Root layout(放 <html>、<body>、Header 等)
├── 📁 (public)/ # Route Group:不需登入的頁面
│ ├──📁 login/
│ │ ├── page.tsx # "/login"
│ │ └── 📁 _components/ # Private Folder:只給 login 用的元件
│ │ └── LoginForm.tsx
│ └── 📁 register/
│ ├── page.tsx # "/register"
│ └── 📁 _components/
│ └── RegisterForm.tsx
└── 📁 (private)/ # Route Group:需要登入
├── layout.tsx # 權限驗證(server component,未登入就 redirect)
├── 📁 user-profile/
│ ├── page.tsx # "/user-profile"
│ └── 📁 _components/
│ └── ProfileCard.tsx
└── 📁 my-favorite-list/
├── page.tsx # "/my-favorite-list"
└── 📁 _components/
└── ListTable.tsx
這裡使用 App Router 的 Route Groups 建立了 (public) 和 (private) 兩個資料夾,將需要登入才可以進入的路由和沒有登入就可以進入的路由區分出來。在透過 Private Folders 的寫法,把特定路由材會用到的元件透過 _components 資料夾整理再一起。
這樣調整過後,比起原本的版本,整個資料夾的結構就變得更好懂,管理上也會更加便利。
最後也快速總結一下今天說到的這兩種路由的進階用法。
當有需要將路由進行分類管理,但不能影響到路由時,可以在資料夾名稱上加上 ()
來建立 Route Group。像是頁面權限如果有差異,或是 UI 上有差異,都可以用建立 Route Group 來分類。
當有某些邏輯或是元件只會使用到特定頁面底下時,則可以在 app 資料夾的特定 router 資料夾下,透過加上 _
來命名資料夾,以建立 Private Folder。透過這樣命名方式建立的資料夾,不會被建立成一個路由,但是能更好地管理元件和邏輯,且更明確地知道這些元件和邏輯是專屬於那個路由的內容。