前言
為了重寫我們的德國電商網站,我進行了深入評估,並決定採用 Next js 的最新穩定版本13.4.3,這個版本的 SEO meta api和server side component功能具有極高的吸引力,且加上未來許多 NextJS的功能都將以app route 上,因此我們選擇採用app route對我們來說毫無疑問。
1 Server side component
1.1 App folder 下的元件或頁面,預設都是 server side component
-
By default, all components on NextJS 13 inside the App folder are server components. And Server Components cannot use client features such as useState, useEffect, etc.
-
For now, to use third-party components the solution is to create a wrapper for each client component that doesn't include the directive 'use client':
1.2 Server side component 好處
Server Components的運作方式是在伺服器上進行渲染,從而只傳遞需要的JS Bundle程式碼至用戶端瀏覽器,不必要的js則不會被下載,這個特性能有效地減少網路傳輸量,提高效能和速度。
1.3限制
不能使用 useState 和 useReducer、 Hooks、useEffect、useLayoutEffect
1.4 如果要使用 clinet side component ,每個 component 都要加上這一行
"use client";
import xxx
...
2 新 useRoute
import { useRouter, useParams, usePathname ,useSearchParams } from 'next/navigation';
2.1 在新的 useRoute ,並沒有 locale
const { locale } = useRouter();
2.2 路由
2.2.1 資料夾結構
這種改動的方式,更直觀從資料夾結構了解是頁面還是元件。
| Url | Page route | App route |
|---|---|---|
| / | /page/index.tsx | /app/page.tsx |
| /about-us | /page/about-us.tsx | /app/about-us/page.tsx |
P.S. 附錄有寫了一個Powershell 快速將 page folder 遷移到 app folder
2.2.2 api 路由
| Page | Route 2 | Result 3 |
|---|---|---|
| app/page.js | app/route.js | Conflict |
| app/page.js | app/api/route.js | Valid |
| app/[user]/page.js | app/api/route.js | Valid |
app/products/api/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 });
}
2.3 Data Fetching 方式調整
2.3.1 新的寫法並不透過 getStaticProps、getServerSideProp 去要資料
async func getData() {
const res = await fetch ("https://api.xxx.com/...");
return res.json();
}
export default async function About() {
const name = await getData();
return "...";
}
2.3.2 新增 use hook 可以將非同步的結果回傳回來
use(getData(id))
2.3.3 server side 改寫了元生的 fetch api ,並預設有cache 機制,且官方並不建議包裝fetch 在client component
2.3.4 revalidate
SSG:頁面預設就是 SSG
SSR:在元件宣告為非同步,且請求時必須關閉緩存,並在請求參數中的cache欄位設置為 no-store、no-cache 或者設置revalidate 為 0 的時候,才會是動態服務端渲染。
CSR:在使用 "use client" 的客戶端元件中,在Use Effect 後請用的渲染的元件
ISR:在 fetch 請求中設置 revalidate,或者在 page.tsx 宣告 revalidate。
app/page.tsx
export const revalidate = 60; // revalidate this page every 60 seconds
P.S. 過去的 pages/api/revalidate 的web hook 仍然有效,但要在page fodler
3. 提供 meta api 替代 next/head,更容易的針對每個頁面給不同 seo 的 meta data
3.1 Static Metadata
import { Metadata } from 'next';
export const metadata: Metadata = {
title: '...',
description: '...',
};
export default function Page() {}
3.2 Dynamic Metadata
import { Metadata, ResolvingMetadata } from 'next';
type Props = {
params: { id: string };
searchParams: { [key: string]: string | string[] | undefined };
};
export async function generateMetadata(
{ params, searchParams }: Props,
parent?: ResolvingMetadata,
): Promise<Metadata> {
// read route params
const id = params.id;
// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json());
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || [];
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
};
}
export default function Page({ params, searchParams }: Props) {}
4 昇級後受到影響的套件
4.1 i18n 套件 ( 推薦 )
剛昇級完後就會發現相關的 i18n 套件無法使用,新版 useRouter 也不提供 locale 可以使用,試了好幾個 i18n 套件,發現這個套件最好用
import { useLocale } from 'next-intl';
const locale = useLocale();
4.2 react query 注入方式
4.4 context api 載入方式
5 Turbopack( 仍在 Beta )
最後可以持續觀注的,Next.js 13 加了一個名為 Turbopack 的新的 JavaScript 打包工具,它被稱為 Webpack 的繼承者,Turbopack 由 Webpack 由 Rust 撰寫,號稱比原始 Webpack 快 700 倍(並且比 Vite 快 10 倍)。
6. 結論
無可否認,Next js 的更新速度令人驚訝,經常在我醒來之後就有了新的穩定版本,每次改版都會有點小陣痛,但大概都花個一至兩天就能昇級,也保留舊的寫法。
app route 改動的幅度有點大,且有點痛,但 app route出現,正式掀開 server side component 的序幕。
附錄 - 一些快速昇級的 powershell
1.寫了一個 powershell 快速將 page folder 搬到 app folder
# Get a reference to all tsx files in the src\pages directory.
$files = Get-ChildItem -Path "src\pages" -Filter "*.tsx"
# For each file, create a new directory in src\app with the same name as the file (without extension),
# move the file to the new directory, rename it to page.tsx, and prepend 'use client;' to it.
foreach ($file in $files) {
# Create new directory.
$newDir = New-Item -Path "src\app\$($file.BaseName)\" -ItemType Directory
# Move and rename the file.
$newFile = Move-Item -Path $file.FullName -Destination "$($newDir.FullName)\page.tsx" -PassThru
# Add 'use client;' to the beginning of the file.
$content = Get-Content -Path $newFile.FullName
$newContent = 'use client;' + "`n" + $content
Set-Content -Path $newFile.FullName -Value $newContent
}
pause
2.用 powershell 快速將元件轉成 client side 元件的
# Get a reference to all tsx files in the src\pages directory.
$files = Get-ChildItem -Path "src\pages" -Filter "*.tsx"
# For each file, create a new directory in src\app with the same name as the file (without extension),
# move the file to the new directory, rename it to page.tsx, and prepend 'use client;' to it.
foreach ($file in $files) {
# Create new directory.
$newDir = New-Item -Path "src\app\$($file.BaseName)\" -ItemType Directory
# Move and rename the file.
$newFile = Move-Item -Path $file.FullName -Destination "$($newDir.FullName)\page.tsx" -PassThru
# Add 'use client;' to the beginning of the file.
$content = Get-Content -Path $newFile.FullName
$newContent = 'use client;' + "`n" + $content
Set-Content -Path $newFile.FullName -Value $newContent
}
pause
3. 用Powershell 遍歷資料夾下的Json 合併成在一個新的 JSON 檔案
# 初始化空的 Hashtable
$combinedJson = @{}
# 指定資料夾路徑
$folderPath = 'C:\path\to\json\files'
# 尋找所有 .json 檔案
Get-ChildItem -Path $folderPath -Filter *.json | ForEach-Object {
# 獲取檔案名稱並移除 .json 副檔名
$propertyName = $_.BaseName
# 讀取 JSON 檔案內容
$content = Get-Content $_.FullName | ConvertFrom-Json
# 將 JSON 內容加入到 combinedJson 中,使用檔案名稱作為屬性
$combinedJson[$propertyName] = $content
}
# 將合併的 JSON 轉換為字符串並寫入到一個新的 JSON 檔案
$combinedJson | ConvertTo-Json | Set-Content -Path "$folderPath\combined.json"






















留言