[NextDoc] Getting Started
- Create a Next.js App @ next.js > basic
- Examples @ next.js
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"
Navigation
- 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 />
- Static File Serving @ Next.js > Docs > Basic Features
- Image Optimization @ Next.js > Docs > Basic Features
-
靜態檔案會放在
/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
- Built-In CSS Support @ Next.js > Docs > Basic Features
- 在 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.js
中import
全域的 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 內:
// 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 灌進了的資料
// 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
- TypeScript @ Next.js Doc
- TypeScript Example @ Next.js Github
在根目錄新增一支 tsconfig.json
的檔案。接著再啟動專案時,Next.js 即會在終端機中提示要安裝的其他套件。