認識 Next.js 13 ( 一 ):什麼是 Next.js & Next.js 13 專案建立

Sam Chang ( S.C )
18 min readOct 2, 2023

--

在開始介紹 Next.js 13 改版內容以前,先來介紹一下 Next.js:

當我聽到一個新名詞,在 dive in 前,第一步我一定會問自己:要怎麼用一句話來解釋這個名詞?所以該怎麼用一句話說明 Next.js 是什麼呢?

我會說 Next.js 是 Vercel 在 2016 年推出的全端框架

看似簡短的一句話,其實裡面包含了三個重點:

Vercel

開發 Next.js 的團隊,請好好記住這個名字,未來可能時不時會看見它。

「框架」

相信這兩個字,在各位軟體工程師們的世界中如影隨形,為什麼要特別強調框架這個概念呢?事實上在 Next.js 官方的 Tutorial 的第一部分 What is Next.js,就特別將 framework 這個詞設為粗體。這時你可能會聯想到,常有人會說 React 是一個 Javascript “library” 不是一個框架,到底兩者差在哪裡呢?

開發一個 app 包含了許多面向,像是 UI、routing、data fetching、rendering、部署等等,而一個「框架」應該要能全面地 cover 這些需求。假如習慣使用像是 create-react-app 這些 boilerplate 來建立 React 專案,可能會以為 React 本身一手包辦了所有事情。但其實打開 create-react-app 專案的 package.json 檔案,你會發現它只幫你先裝好了常用的套件,像是 React-DOM、Webpack 等等。事實上 React 本身只負責 UI 的建造,其餘工作必須交給其他第三方套件執行,像是 React-DOM 負責真實操控 DOM、React-Router 負責路由切換等等,因此會說 React 只是一個 library。

Libray 好處是讓專案的自由度高,開發者可依需求自由挑選工具,像是可使用不同 React Renderer (ex: React Native、React TV,有興趣可參考 CT 大的 awesome-react-renderer GitHub repo) 將 UI 渲染到不同裝置上,且通常與第三方套件的相容性也會比較高;但自由度高也意味著需額外花時間精力選擇、設定各式工具。

相較於 library,框架則提供較為完整的 solutions,而 Next.js 除了整合 React.js 負責 UI 建造外,也提供了 routing、rendering、data fetching、intergrations 等任務的解決方案。

Next.js is a React framework that gives you building blocks to create web applications.

By framework, we mean Next.js handles the tooling and configuration needed for React, and provides additional structure, features, and optimizations for your application.

Next.js — What is Next.js

但 React 到底是個 library 還是框架,其實一直存在不同的觀點。像今年 React core team 的成員之一 Sebastian 在接受 Vercel 的訪談時,提到 React 也是用 framework 這個詞,他也說他沒很在意到底該說 library 還是 framework,所以這部分或許挺見仁見智。

「全端」

相較於 React、Vue、Angular 等前端框架,Next.js 同時支援 server-side 和 client -side 的開發環境,也因此可在同個 codebase 跑一些後端功能,像是 Pre-Rendering、Server Components、寫 API endpoints 等,提供開發者在 SEO、效能優化、資料保密等議題上有更多選擇,後續文章會再詳細介紹!

雖然官方是用 full-stack framework 這個詞,但 Next.js 只能支援叫基礎的後端需求,像簡單 CRUD,環境也不是這麼完整 (ex: 預設 server 沒有入口點 )。所以假如要做一些較為複雜的後端應用,或 stateful 連線等,可能還是需另起後端環境。

因此相較於「全端」框架,可能會更接近前端 + Backend For Frontend 框架,較適合前端為主,配合一些簡單後端應用的情境。

講到這邊必須澄清一個觀念,Server-Side Rendering 不是只有 Next 可以做到,也不是只有 SSR 才能 SEO。事實上React 其實也可以做到 Server-Side Rendering 與 Server Component,只是使用門檻較高,像是你必須額外建置一個後端 server、處理 Hydration、調整 webpack 打包設定等等,有興趣可以參考官方教學影片,或 Redux 作者,也是 React 成員之一的 Dan Abramov 的教學文章

Client-Side Rendering 的架構也是有優化 SEO 的方式,例如準備一份有完整內容的 HTML,當偵測到 request agent 是搜尋引擎爬蟲,就餵给它這份檔案( 有興趣可參考莫大的文章 )。只是一樣,開發上會相對複雜,這些 solutions 也不一定所有情境下都適用。

而 Next.js 則是在這些需求上,提供更簡單的 solutions,減少在 server-side 渲染內容,以及優化 SEO 開發上的難度。 像預設 Pre-rendering,或像是 Next.js 13 的 components 預設為 Server Components,假如要轉為 Client Components 也只需要簡單一句 ‘use client’ 的註解,這部分後面會介紹。

建立 Next.js 13 App Router 專案

認識完 Next.js 後,我們就來認識 Next.js 13 的改版內容吧!

首先我們先來用 create-next-app 建一個 Next.js 專案:

npx create-next-app@latest

環境需求:

Node.js 16.14 版以上

支援作業系統:macOS、Windows ( 包含 WSL )、Linux

接下來你會依序看到以下幾個提示 ( 問題 ):
1. What is your project named?
輸入專案名稱。須注意,名稱必須 URL friendly,意思是所有字符都必須是 ASCII subset 之一,所以只能輸入英文字母、數字、dash ( — )、底線 ( _ ),不能輸入中文、空白等等。

除此之外,因為 npm 套件的規定,英文字母只能用小寫。有興趣了解緣由可以參考這個 issue

(O):myapp、my-app、my_app
(X):my app、myApp、我的專案

2. Would you like to use TypeScript? No / Yes
TypeScipt 是現在專案開發很常使用的 JavaScript superset,可以在開發 JS 專案時避免許多型別定義或轉換導致的 bug。這邊就不花時間介紹,假如還不認識 TypeScript 的讀者可以直接 Google 關鍵字搜尋!

希望專案支援 TypeScript 就選 Yes,反之則選 No。假如選 Yes,Next.js 就會自動幫你安裝所需的 packages 以及設定檔。後續文章的 sample code 都會使用 TypeScript,假如想複製 sample code 自己跑跑看的讀者可以選 Yes。

3. Would you like to use ESLint? No / Yes
ESLint 是很常見的 linter,能讓開發團隊自訂 coding 規則,統一 coding style。

這部分大家可以自行斟酌,假如習慣使用 ESLint 就選 Yes;假如想使用其他 linter,像是近年呼聲很高的 Rome,則可以選 No 再自己安裝其他 linter。

4. Would you like to use Tailwind CSS? No / Yes
Tailwind CSS 是個近年來很熱門的 CSS 框架,這部分就看個人 styling 工具使用習慣選擇。假如選 Yes,Next.js 會幫你安裝好需要的 packages 與設定檔,以及做好前置設定,基本上專案安裝好就可以直接使用。後續 sample code 會使用 Tailwind CSS,所以一樣,假如想跑跑看 sample code 可以選 Yes!

這邊有個小提醒,後續會提到 Next.js 13 更新內容中一大重點 — Server Components。Server Components 目前並不支援 CSS-in-JS,所以假如習慣使用像是 Styled-Components 的讀者,在選擇 styling 工具時可將這點納入考量。

5. Would you like to use “src/” directory? No / Yes
假如希望 /components、/lib、/utils 等資料夾不要放在根目錄,可以與設定檔 ( ex: package.json、next.config.js ) 隔開,可以選 Yes,在負責 routing 的 /app 或 /pages 外加一層 src 資料夾。

當然上述提到的資料夾也可以放在 /app 中,明天會再跟大家介紹 App Router 的檔案結構,這題可依個人習慣選擇!

但要小心,假如專案中同時有 /app 或 /pages 與 /src/app 或 /src/pages,src 裡的 /app 和 /pages 會自動被略過。

6. Would you like to use App Router? (recommended) No / Yes
App Router 是 Next.js 13 推出的新的路由架構,提供了一系列能提升網頁效能和開發體驗的新功能,之後會再詳細介紹!

這個系列分享的內容是基於 App Router 的架構,所以這邊就選 Yes!但假如個人專案要使用舊有的 Pages Router 模式,則可選 No。

7. Would you like to customize the default import alias? No / Yes
為了使 import 路徑能更直覺,Next.js 提供 “baseURL” 的設定讓開發者可以用絕對路徑的概念來 import modules。

直接來看官方提供的例子會更好理解:

// before
import { Button } from '../../../components/button'

// after
import { Button } from '@/components/button'

預設的 alias 是 @,如上面官方的範例;假如想自訂 alias,比方說 @root,這邊就選 Yes,並在下一題 What import alias would you like configured? 輸入 @root/*

以相同的官方範例來說,Button 的 import 路徑就會改成:

// before
import { Button } from '@/components/button'

// after
import { Button } from '@root/components/button';

預設的 baseURL 是指向專案根目錄,等下會再教大家怎麼改 baseURL 的路徑和自訂不同的匯入路徑!後續 sample code 的 alias 會使用預設的 @,所以這題選 No。

這些都填完了之後按下 Enter,Next.js 就會根據你的回答,安裝好需要的 dependencies 和建置專案結構。

當然你也可以選擇不使用 create-next-app,手動安裝 Next.js。因為篇幅考量,就不再多做介紹,有需求的讀者可以參考官方文件

修改 baseURL

最後我們來回答剛剛的問題:假如想修改 baseURL 和自訂匯入路徑,我們首先打開根目錄中的 tsconfig.json。在 compilerOptions 中找到 paths

{
"compilerOptions": {
...
"paths": {
"@/*": ["./src/*"]
}
},
...
}

假如沒有自訂 import alias,你會看到 @/* 指向 ./src/* 這個路徑。所以假如想將 baseURL 改到 /src/components,就改一下 @/* 的 value:

{
"compilerOptions": {
...
"paths": {
"@/*": ["./src/components/*"]
}
},
...
}

這時同樣以上述官方範例為例,Button 的匯入路徑就會改為:

// before
import { Button } from '@/components/button'

// after
import { Button } from '@/button';

假如想自訂匯入路徑,就在這邊加一個 key-value pair。比方說我新增一個路徑 components/ 可以對應到 ./src/components/

{
"compilerOptions": {
...
"paths": {
"@/*": ["./src/components/*"],
"components/*": ["./src/components/*"]
}
},
...
}

官方範例的 Button 匯入路徑就可以改為:

// before
import { Button } from '@/components/button'

// after
import { Button } from '/components/button';

有興趣的讀者可以自己玩玩看!

App Router 專案架構

用 create-next-app 建好專案後,根目錄預設檔案主要分為三大類:

  1. /public
  2. (/src)/app
  3. 設定檔

/public

專案會用到的靜態檔案 ( 主要是圖片 ) 可以放在 /public 中。優點是當我們要指向這些檔案的路徑時,可以使用 baseURL /

舉例來說,/src/components/Logo.tsx 要插入 /public/logo.svg 這張圖片,圖片路徑只需要指向 /logo.png 就好,不用指向 ../public/logo.png。當專案巢狀結構複雜的時候可以省去很多找檔案相對路徑的時間!

/* src/components/Logo.tsx */
import Image from 'next/image';

export default function Logo() {
return <Image src='/logo.png' alt='logo' width={500} height={500} />;
}

<Image> 是 Next 提供用來提升圖片讀取效能的一個 component,最後幾天會講到。假如想使用一般的 <img> tag 也可以:

/* /src/components/Logo.tsx */

export default function Logo() {
return <img src='/logo.png' alt='logo' />;
}

那圖片以外的靜態檔案,像是常見的 faviconrobots.txtmanifext.json 呢?過往 Pages Router 的路由架構下,這些靜態檔案一樣會被放在 /public 中;但 App Router 的路由架構下,這些 metadata 檔案需要放在 /app 的根目錄,這點假如要從 Pages Router 搬到 App Router 時也要特別注意。

Metadata 和 SEO 我們進階篇也會和大家更進一步討論,我們先進入下一個資料夾 (/src)/app 吧!

(/src)/app

app 資料夾中主要存放和 routing 與 Metadata 相關的檔案。不同於以外 Pages Route 的架構,在 /pages 中的檔案會自動視為 route,App Router 則是當 /app 底下資料夾中含有 page.tsx 才會被視為一個 route segment;或是有 route.ts 會被視為一個 api endpoint。

舉例來說,/app/about/pages.tsx 定義的 UI,以開發模式來說,就會出現在 http://localhost:3000/about 這個頁面中;假如另個資料夾 /app/components 中沒有任何 page.tsx,就不會有 http://localhost:3000/components 這個頁面。

App Router 路由定義範例

( 圖片來源:https://nextjs.org/docs/app/building-your-application/routing/defining-routes )

除了 page.tsx 外,官方還有提供 layout.tsxloading.tsx 等 routing 相關的特殊檔案,也有 dynamic routes、parallel routes 等不同的路由設計,我們後續介紹 App Router 的 routing convention 時會再跟大家細談!

除了 routing 相關的檔案,metadata 會用到的檔案,像是 faviconopengraph-imagerobots.txtmanifest.jsonsitemap.xml 等等,也會放在 /app 的根目錄。

設定檔

最後,設定檔 ( ex: next.config.tstsconfig.jsontailwind.config.ts )、package.json、環境變數、.gitignoremiddleware.tsinstrumentation.ts 這些檔案也會放在專案根目錄。

了解初步的專案架構後,我們接著來看看 app 資料夾預設含有哪些檔案

根目錄會看到 global.csslayout.tsxpage.tsx 三個檔案。

  • globals.css 用來設定全域 CSS 樣式和放 Tailwind 基本設定內容。
  • layout.tsx 是官方提供的 routing 特殊檔案之一,後續會詳細介紹。這邊可以先記得,放在 /app 根目錄的 layout.tsx 我們叫做 Root Layout,用來放所有 route 都會固定出現的 components、設定 metadata,以及修改 initial HTML 的內容。
  • page.tsx 前面有提到,負責定義 route segment 的 UI。/app 根目錄中的 page.tsx 就會是 / 的 UI,可以想像成首頁內容 ( 除非你的首頁是 /home 之類的 )。

比方說我的網站網域是 https://my-next-app.io ,那 /app 根目錄的 page.tsx 就會是https://my-next-app.io 的頁面內容!

認識完這些檔案後,你可能會問,那我該怎麼進入開發模式,看我寫的網頁長怎樣呢?我們打開 package.json,會看到官方幫你建了四個 scripts:dev、build、start、lint。

{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
}
}

只要在終端機輸入 npm run dev,就能進入開發模式了,當然你也可以調整成其他指令。預設的 port 是 3000,假如想自訂 port 的話,可在 dev script 最後加入 --port=XXXX

"scripts": {
...
"dev": "next dev --port=5500",
...
},

也簡單說明一下另外三個 scripts 的用途:

  • build:打包程式碼
  • start:進入 production mode ( 須先跑完 build )
  • lint:跑 Next.js 內建的 ESlint 檢查

檔案結構範例

最後,你可能還是有點困惑,到底什麼檔案該放在 /app 裡,什麼該放在 (/src) 根目錄?

按照官方說法,除了上面提到的 /public 和設定檔必須放在根目錄外,其餘像是 /components/utils/lib 等等共用工具,並沒有強制必須放在哪邊,可以依照個人或團隊習慣來設計。

Apart from routing folder and file conventions, Next.js is unopinionated about how you organize and colocate your project files.

Next.js — Project Organization and File Colocation

假如想要一些檔案分類方式的參考,Next 官方有提供三種範例:

  1. /app 中只放 routing 相關的檔案,其餘放在 ( /src ) 根目錄,類似以往 Pages Router 的作法。

2. 所有 source codes 都放在 /app 裡 ( 就不需要 /src 了 ),與設定檔分離。

3. 依照 routes 或 features 分類,各自有各自的 /components/lib 等等。

( 圖片來源:https://nextjs.org/docs/app/building-your-application/routing/colocation )

以上是關於 Next.js 和 App Router 專案結構的基本介紹,下篇開始就會正式進入 Next.js 13 的改版內容介紹!

--

--