在「錯誤捕捉、全域 CSS、共用 Layout,就用 _app.tsx 來搞定吧!」這邊文章中有提到,目前在 Next.js 的 error boundary 無法捕捉到 SSR 發生的錯誤,會一律回 HTTP 500 給使用者,因此使用者會看到相對應的 500 畫面。
而在 Next.js 客製化 404 與 500 的頁面是很容易的一件事,就像是 App 與 Document 一樣,都是在 pages/
這個資料夾裡面依照規範定義檔案名稱,就可以輕易地客製化這兩個頁面。
可以透過
getStaticProps
在 SSG 階段傳入一些資料
要客製化 404 的頁面必須在 pages/
資料夾中新增一個檔案為 404.tsx
,這個檔案將會以 SSG 打包成靜態的 HTML 檔案:
// pages/404.tsx
const Custom404 = () => {
return <div>Custom 404 page</div>;
};
export default Custom404;
如果一個 url 無法匹配 file-based routing 的機制,或是在 getStaticsProps
與 getServerSideProps
回傳 { notFound: true }
,伺服器就會直接回傳這個 404 的頁面,不用再經過其他的運算。
可以透過
getStaticProps
在 SSG 階段傳入一些資料
要客製化 500 的頁面必須在 pages/
資料夾中新增一個檔案為 500.tsx
,這個檔案將會以 SSG 打包成靜態的 HTML 檔案:
// pages/500.tsx
const Custom500 = () => {
return <div>Custom 500 page</div>;
};
export default Custom500;
像是 getServerSideProps
在運行時發生錯誤,或是頁面中發生錯誤都可能會造成 500,此時 Next.js 的伺服器就會回傳這個頁面給使用者。
這個頁面是用來處理除了 404 跟 500 以外的錯誤,而且同時包含客戶端與伺服器端錯誤。 React 的 error boundary - componentDidCatch
無法處理 SSR 發生的錯誤,但是在 Next.js 中可以經由這個檔案來捕捉錯誤,並回傳相對應的訊息給使用者。
雖然 error page 的作用跟 error boundary 很像,但是 error boundary 真正要做的事情是透過 **componentDidCatch
捕捉錯誤本身**,你可以對錯誤本身進行處理。然而在 error page 中假設遇到的是客戶端的錯誤,實際上只是用來告訴使用者有錯誤,可是這樣一定不能滿足工程團隊的需求,因為工程團隊需要知道使用者實際上遇到的錯誤,所以需要使用 error boundary 來捕捉錯誤。
如此一來,在 Next.js 中其實可以考慮切分錯誤處理的職責,像是 error boundary 負責捕捉錯誤,並且紀錄錯誤訊息至伺服器,但是並不會處理顯示的職責,而顯示錯誤訊息則交給 error page,這樣才不會讓錯誤處理的職責散落程式碼各處,也可以讓後續維護的人更快速理解程式碼的架構。
import { NextPageContext, NextPage } from "next";
interface ErrorProps {
statusCode?: number;
}
const Error: NextPage<ErrorProps> = ({ statusCode }) => {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: "An error occurred on client"}
</p>
);
};
Error.getInitialProps = ({ res, err }: NextPageContext) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
return { statusCode };
};
export default Error;
404 跟 500 的客製化頁面現在都是以 SSG 的方式打包成靜態的 HTML 檔案,可以免除 SSR 需要花時間渲染的成本。由於是 SSG,所以想要傳入資料至 404 或 500 的頁面中目前暫時做不到,有看到 GitHub discussion 有一篇在講述希望可以提供這個功能,未來也許有一天能看到這個功能被實現。
在 Next.js 中除了能夠客製化 404 跟 500 頁面之外,也可以客製處理其他錯誤的頁面,而且同時包含客戶端與伺服器端的錯誤。error page 的功用可以跟 error boundary 做切分,error page 處理顯示頁面,error boundary 處理攔截錯誤做進一步的處理,所以兩者並不會相沖。