跳至主要内容

[NextDoc] Getting Started

TD;DR

# 檢視所有 Next.js 提供的指令
$ npx next --help

$ npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"

Routing

  • Next.js 會自動處理 code splitting、client-side navigation 和 prefetching(production 時)的功能
    • code splitting 指的是只會下載使用者有用到的(有檢視到的)頁面,Next.js 會自動做 code-splitting 並且只傳送使用者有看到的頁面到 client。
    • client-side navigation 表示它不是真正透過 HTTP Request 來達到換頁的,而是透過 JavaScript 讓使用者所看到的頁面改變,因為瀏覽器不需要整個重新 reload,所以速度會比較快
    • prefetching 指的是 next.js 會根據該頁 <Link /> 中有用到的頁面,預先把檔案下載下來,減少使用者在頁面切換間所需等待的時間
  • 如果希望在連結上添加 HTML 屬性,例如 className,需要加在 <Link /> 元件中的 <a /> 元素,而不是放在 <Link /> 上:
// https://github.com/vercel/next-learn-starter/blob/master/snippets/link-classname-example.js

import Link from 'next/link';

export default function LinkClassNameExample() {
// To add attributes like className, target, rel, etc.
// add them to the <a> tag, not to the <Link> tag.
return <Link href="/">Hello World</Link>;
}
注意

如果在 next.config.js 有設定 basePath 的話,一定要使用 next/link 提供的 Link 元件,如果直接使用 <a /> 的話,連結的網址不會自動加上 basePath 作為前綴。

Assets, Metadata, and CSS

Assets

keywords: <Image />
  • 靜態檔案會放在 /public 資料夾中。

  • 透過 Next.js 提供的 Image 元件,可以自動

    • 根據不同的螢幕尺寸來 responsive 的顯示圖片
    • 最佳化圖片(變更尺寸、最佳化、在瀏覽器支援的情況下使用 WebP)
    • 只有在圖片進入 viewport 時才下載該圖片
  • 既使圖片是來自外部(例如,CMS),Next.js 一樣可以針對這些圖片進行優化;此外,Next.js 會在圖片需要被使用時(例如,瀏覽器發送請求時)才進行優化的動作,而非在 build-time 就把圖片都處理完,因此不會因為圖片的數量變多而使得 build time 的時間增加。

Metadata

keywords: <Head />

Customize Document @ Next.js > Docs > Advanced Features

  • 透過 Next.js 提供的 <Head /> 元件,可以改變 HTML 頁面中 <head /> 的內容。

Third-Party JavaScript

keywords: <Script />

使用 next/script 可以用來載入第三方的 JavaScript 檔(例如 Facebook SDK),它提供了一些 helper 可以使用:

// https://nextjs.org/learn/basics/assets-metadata-css/third-party-javascript

export default function FirstPost() {
return (
<>
<Head>
<title>First Post</title>
</Head>
<Script
src="https://connect.facebook.net/en_US/sdk.js"
strategy="lazyOnload"
onLoad={() => console.log(`script loaded correctly, window.FB has been populated`)}
/>
<h1>First Post</h1>
<h2>
<Link href="/">
<a>Back to home</a>
</Link>
</h2>
</>
);
}

CSS Styling

  • 在 Next.js 中預設就支援 CSS Modules、SASS 和 styled-jsx 的寫法,若有需要也可以使用 styled-components、emotion、Tailwind CSS 等等。
  • 若需要使用全域的 CSS 樣式,需要建立 pages/_app.js 這支檔案。這支檔案將會是最上層的元件,可以橫跨不同的頁面:
// pages/_app.js
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
注意

當新增 pages/_app.js 的檔案後,需要重新啟動伺服器。

備註

App 元件因為是所有元件的最上層,因此除了可以用來作為全域的 CSS 樣式使用外,也可以用來保存全域的狀態。

  • 建立全域的 CSS 樣式,在根目錄建立 styles 資料夾,並在裡面建立 global.css 的檔案:
/* styles/global.css */
html,
body {
/* ... */
}
  • 最後在 pages/_app.jsimport 全域的 CSS 樣式:
import '../styles/global.css';

export default function App({ Component, pageProps }) {
return <Component {...pageProps} />;
}
注意

全域的 CSS 樣式只能在 pages/_app.js 中被載入,否則它可能會無預期的影響到其他頁面。

Pre-rendering and Data Fetching

Pre-rendering and Data Fetching @ next.js > basic

根據資料的使用選擇不同的 render 方法:

  • SSG:畫面(HTML)/資料能夠在 build-time 時就取得(不論是來自 fetch, database 或實體檔案),並且不會每次 request 都改變,畫面在 build time 時就已經建立好,建議大部分情況使用。使用 getStaticProps
  • SSR:畫面(HTML)/資料能夠在 pre-render 時(即,每次收到 request 時)取得,且每次內容都會不同。使用 getServerSideProps
  • CSR:資料不需要 pre-render 時,像是 Dashboard 這類不需要考慮 SEO 的情況。畫面完全由 JavaScript 產生。

Hydration

預設的情況下,Next.js 會 pre-render 所有頁面,也就是會預先針對每個 Page 產生對應的 HTML,而非透過 client-side JavaScript 來做這件事,以此達到個好的效能和 SEO。

每一個產生好的 HTML 頁面都會伴隨部分的 JavaScript,當一個頁面被瀏覽器載入後,這些 JavaScript 的程式碼會執行,以讓頁面能夠帶有完整的互動功能(這個過程稱作,hydration

Static Site Generation (SSG) 和 Server-side Rendering (SSR)

實際上,Pre-rendering 分成兩種形式,分別式 **Static Generation(SSG / Static Site Generation)**和 Server-side Rendering(SSR)這兩者間最重要的差別是靜態頁面(HTML)產生的時間點

提示

SSR 和 SSG 兩者最大的差別是靜態頁面生成的時間點。

  • Static Generation:靜態頁面是在 build-time 時就產生,伺服器收到 request 時重複使用這些已經生成好的靜態頁面。
  • Server-side Rendering:伺服器每次收到請求時在產生對應的靜態頁面。
注意

在開發模式(development mode)下,Next.js 都會採用 Server-side Rendering,即是你設定使用的是 SSG。

在 Next.js 中,可以根據每個頁面選擇要使用 SSG 或 SSR,不一定要全站統一。

Static Generation with Data

keywords: getStaticProps
  • 當你在匯出一個 Page 時,可以同時匯出一個名為 getStaticProps 的 async 函式,這個函式將會在 build time 時(production mode)被執行,透過這個名為 getStaticProps 的 function,等同於是告訴 Next.js 說:「這個頁面需要相依於某些外部的資料,因此當 pre-render 此頁面時,需先確定已經取得這些資料(resolve)」。
  • getStaticProps 是一個 async 的函式,你可以在這個 function 中去 fetch 外部的資料,並且把它當成 props 灌入頁面中。
  • getStaticProps 的內容只會在 server-side 被執行,並不會在 client-side 被執行,也不會被打包進傳送到瀏覽器端的 JS 檔,因此你也可以在這裡直接建立與 Database 間的連線。
    • 由於 getStaticProps 這個函式的目的是在 build time 時執行,因此你不能使用那些只有在發送請求後才能取得的資料,像是 query parameters 或 HTTP headers。
  • getStaticProps 只能從 Page 元件被 export
備註

在開發模式(development mode)下,getStaticProps 會在每次收到 request 時被執行。

將資料從 server 透過 props 灌進 component 內:

pages/index.tsx
// https://nextjs.org/learn/basics/data-fetching/implement-getstaticprops
import { getSortedPostsData } from '../lib/posts';

export default function Home() {
return (
// ...
);
}

export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}

在 component 中透過 props 取出 server 灌進了的資料

pages/index.tsx
// https://nextjs.org/learn/basics/data-fetching/implement-getstaticprops
import { getSortedPostsData } from '../lib/posts';

export default function Home({ allPostsData }) {
// we can use allPostsData in the component
return (
// ...
);
}


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

特別需要留意的一點是,在 product mode 才會時真正的 SSG,因為:

  • 在 development mode 時,getStaticProps 會在每次收到 request 時執行,就和 getServerSideProps 類似
  • 在 production mode 時,getStaticProps 會在 build time 就執行完。

Fetching Data at Request Time(SSR)

keywords: getServerSideProps

若你需要的資料無法在 build time 時就取得,那麼應該考慮使用 SSR 的方式,這時候會用到的方法是 getServerSideProps 而不是 getStaticProps

因為 getServerSideProps 會在收到 requests 的時候才被呼叫,所以可以它參數中的 context 會有和 request 有關的訊息可以使用。

Client-side Rendering (CSR)

如果你不需要在 pre-render 時就帶入資料,那麼則可以考慮使用 client-side rendering。Next.js 團隊有寫了 SWR 這個套件可以參考。

Data Fetching Strategies

一般來說,可以從「每個使用者看到的頁面是不是相同的」來做考量,如果頁面的資料會有頻繁的更新,且內容會根據每次的請求而有不同時,在這種情況下就可以選擇使用 SSR 或 CSR。

否則,一般會建議使用 ISG 或 SSG,因為頁面只需要建立一次後,就可以透過 CDN 來提供,速度會比 SSR 來得更快。

提示

只要你認為這個頁面可以是在瀏覽器發送請求前就先產生好的話,就使用 SSG。

SSG

  • 使用 getStaticProps
  • 在 build time 時就把需要的資料都拿好
  • 使用者瀏覽頁面的速度較快,SEO 也更好

ISG

  • 使用 getStaticProps + revalidate
  • 擁有所有 SSG 好處
  • 能夠在一定時間後向 server 拿資料,並重新產生靜態頁面
  • 缺點是在不是純的 SSG,所以不能使用 next export 當成純靜態頁面來部署

SSR

  • 使用 getServerSideProps
  • 每一個 request 來都會重新產生新的頁面

CSR

  • 直接在 client side 呼叫後端的 API 來拿資料
  • client 拿到的會是整包後端回傳的 API 資料,資料傳輸量較大
  • 會透露後端的 API 給 client 知道

CSR + API Routes

  • Client 不需要直接呼叫後端的 API,而是呼叫中間層(API Routes)來拿資料
  • Client 拿到的資料可以先經過過濾,資料傳輸量較小,速度較快
  • 可以保護後端的 API endpoint 不讓 client 知道

Dynamic Routes

Dynamic Routes @ next.js > basic

  • 使用 getStaticPaths() 把所有 dynamic routes 的 key 透過 paths 給入
  • 使用 getStaticProps() 把 path 中透過 params 傳進來的參數來取得該頁面的資料內容
// pages/posts/[id].js

// 使用 `getStaticPaths()` 把所有 dynamic routes 的 key 透過 `paths` 給入
export async function getStaticPaths() {
const paths = getAllPostsIds();
return {
paths,
fallback: false,
};
}

// 使用 `getStaticProps()` 把 path 中透過 `params` 傳進來的參數來取得該頁面的資料內容
export async function getStaticProps({ params }) {
const postData = await getPostData(params.id);
return {
props: {
postData,
},
};
}

export default function Post({ postData }) {
// ...
}

API Routes

API Routes @ Next.js > Docs

API Routes 的目的是透過 Next.js 來寫 API endpoint,類似 express 撰寫的 API endpoint 一樣,可以根據不同的 request 回傳不同的 response。

TypeScript

在根目錄新增一支 tsconfig.json 的檔案。接著再啟動專案時,Next.js 即會在終端機中提示要安裝的其他套件。