Next.js 入門:從 CRA 與 Prerender 進化至 Next.js 的歷程


Posted by SimonAllen on 2021-07-13

此文不會講解為什麼 SSR 對 SEO 和 LCP 有幫助的原因
如果不懂 LCP 是什麼,可見 Google 關於 LCP 指標的說明
如果連 SPA 和 SSR 是什麼都不太懂,可以參考 Huli 大大寫的跟著小明一起搞懂技術名詞:MVC、SPA 與 SSR

前言

前陣子的 React 生態圈可謂是振奮人心,官方在今年 6 月初公布了 18 版的計畫,其中基於 SuspenseServer Side Rendering 架構另許多開發者眼睛一亮,畢竟從 Client Side Rendering (以下簡稱 CSR)跨到 Server Side Rendering (以下簡稱 SSR)的過程,是許多前端工程師心中的坎之一。

不少工程師學習 React 時會以 Create React App (以下簡稱 CRA) 入門,開始工作後也自然地用其建構新專案。

CRA 很棒..但?

CRA 是 React 官方推薦的 Toolchain 之一,一般用其開發 Single Page Application (以下簡稱 SPA),某些時候我們會需要 SSR 處理 SEO 需求,甚至專案大了要優化 LCP,這時使用 CRA 為底開發的 SPA 就不是一個太好的選擇。

產品持續迭代時轉換的成本

在已上線且持續迭代的中大型專案,工程師接到需求要手刻 renderToString 調整成 SSR 過程中,會遇到不少問題與成本:

  • 什麼狀態要在 Server Side 就先定義?
  • 那些狀態需要 Hydrate?
  • 原本的 Feature 不用做了嗎?
  • 誰來盤點各頁情境與狀態?
  • 過程中會不會影響原本 user 的操作?

專案越大考慮越多,從 CSR 手刻改成 SSR 心智負擔越大,也因此 React 官方的 SSR 新架構出來,不少人都好奇 React 的 SSR 會走向何方。若只是為了 SEO,又不想大規模改動舊 code,Prerender 或 react-snapshot 都不失為一種解決手段。

Prerender

筆者前公司就是使用 Prerender 架構來處理舊有 CSR Web 的 SEO。以 Prerender 來做 pre-rendering,目的是給 Google 或其他搜尋引擎的爬蟲 Bot 看到渲染完的靜態 HTML,正常使用者仍是照舊 CSR。

  • Prerender 為此套件名稱
  • pre-rendering 或 pre-rendered 為中文所說的「預渲染」

理想流程,圖片來源: netlify

Prerender 不錯,解決了 SEO 的問題,只是一但進入公司商業的場域,中小企業使用 prerender.io 來處理 Prerender 服務,最終還是會遇到付費的問題,所以一般大家會自己架 Middleware Server 來處理。

而這個 service 可能會是建在另外的 Node.js Server 上,至於判斷 user agent 的動作可以讓 Nginx 處理。從結果來看就是整個發出 request 到最終 render Web 的架構都變複雜。

若今天需要處理 SEO 或 Prerender render 相關 bug,就要思考問題發生在流程的哪一段,這已不是單純前端工程師能解決。

開新的 Web Project 前先思考一下

除非本來需求就是「後台」專案,若用 CRA 開新的「前台」專案或「活動類網站」再抱著「後面遇到要 SEO 需求再處理」心態,當專案進入一定規模後,後續修改皆注定複雜度的提升,只是升多升少而以。

如果一開始就有想到有 SEO 需求,尤其是那種不到 10 的研發團隊,選一個舒服的 Toolchain 就能節省未來很多時間,而 Next.js 和 Gatsby.js 就都是受歡迎的選擇。

Next.js

Nextjs

Next.js 是由 Vercel Inc. 開發的 React Toolchain,國際上有不少公司如:twitchIGNMarveltiktok..等等,都是用 Next.js 來建構網站。

全端 Framework?

若用 CRA 開發,最終 build 出來的僅是前端的 SPA 檔案,實際上線 deploy 和 hosting 的 Web Server 可以是各個語言,而 Next 則同時包含了後端 Node.js,按需求亦可視其為完整的全端 Framework (前端 React.js、後端 Node.js)。

Next.js 已幫開發者處理掉不少前後端功能的實作,能做到開箱即用的 SSR,且它仍允許開發者繼續使用 React 生態圈各種元件,如果 CRA 專案還不大,開發者從 CRA 改成 Next 專案負擔也不會太大。

此外開發者也可以直接在其 Web Server 與 database 互動,將其特定 router path 拿來出 API 也是可行的。

Next 的 pre-rendering mod

目前 Next.js pre-rendering HTML 時區分兩種模式:SSGSSR,而實際 build code 時,會發現 terminal 或 log 顯示除了上述兩者,還多了 StaticISR 兩種。

SSG - Static Site Generation

Next.js 9.3 版才加上了 SSG 的功能,SSG 會在專案 server 的 build time 時建構 static HTML 檔案,因為檔案先建好且可被 CDN 快取住,使用者載入體驗會相對好一點。缺點是,HTML 和對應內容在 build time 時建構寫死,所以每次內容更新都要整個專案重 build 一次,不適合常變動網頁。

假如公司網站有分前後台,前台 SSG 構建 HTML 內容是接後台連動的 API (例如產品上稿後台或文章上稿後台)

當行銷人員改動後台上稿內容,後台內容影響對應出的 API 內容改變,前台網頁內容仍然會是舊的,因為前台專案上次重 build 和 deploy 可能是好幾天前,已經 SSG build 好的頁面要改動內容就要重 build 再 deploy 一次。

SSR - Server Side Rendering

SSR 會在專案 server 的 run time 時運作,當 Client 發 GET HTML request 進來,按照對應 path 和 params 在 Server Side 打對應 API,建構完 HTML 檔案才吐給 Client。

初次使用 SSR,可先預期2點:
  • 換頁一樣會有等待的時間差

    在前後端分離,另獨立 API Server 的情境下,以往 CSR 的 SPA,在 Client 打 API 等內容回來再 render,這個過程會有個時間差,為了這幾(毫)秒,大家往往會塞個轉圈圈 Loading 動畫或 placeholder 以提示 user 等待。

    換成 SSR 後,打 API 取 data 與 render 的時機移到 Server Side 去,等待 API 響應的幾(毫)秒時間還是會存在,等 Server run time 組好 HTML 才會吐給 Client,因此建議加上如 nextjs-progressbar 之類的 Loading UI 好提示 user 正在等待 HTML 載入,而不是讓 user 看瀏覽器分頁預設的 Loading 轉圈圈乾等。

這裡的 progressbar 指的是換頁時從瀏覽器上方由左至右的讀取條。
當在 Youtube 進入其他頁面時,會有個讀取的載入條提示下頁正在載入中。

  • 會增加 Web Server 負擔

    Next.js SSR 是在 Server run time 打 API dynamic 組成 HTML 再回傳,如果今天有萬人同時瀏覽萬件不同商品,走 SSR 模式的 Web Server 要多少 API 組 HTML 再一個一個吐回去呢?

    原本 CSR 在 Client Side 做的動作、算力、時間,這些成本 SSR 只是丟給 Server Side 負擔,時間算力成本不會因此消失。所以 Next.js 也建議,只要組成頁面內容的來源不常變動,就建議轉成 SSG 比較好。

注:在 Next.js 架構下,這裡的 Web Server 皆指的是後端 Node.js Server

Static

就是 Static HTML。若不要用 SSG 產生,而是直接在 Next.js 加上寫死的靜態 HTML 檔案也是可以的。

ISR - Incremental Static Regeneration

SSG 在 build time 時建立,萬一該頁數量很多,例如:媒體網站的新聞頁,假如光新聞總計 10000 頁,build code 時 SSG 不就 build 到天荒地老?

ISR 就是為了解決此問題的混合型 SSG 多頁數解決方案,我們可以指定特定常見的頁面,先將其用 SSG 型式 build 出來(例如先產生新聞頁 10000 頁中常見的 2000頁),其他沒 build 到的等未來 request 進來,才在 Server 重新 SSG 構建該頁。

使用 ISR 好處,圖片來源 Next.js

接著 ISR 可以設定快取時間,若有其他的 request 進來,ISR 可以先返回之前 build 好的舊 HTML,接著在 Server 重新構建該頁,未來再回傳新的 HTML 更新。 中小專案通常不太用到 ISR,會用到代表此專案已進入一定規模,有非常多的頁數才需要如此。

不過不管是上述哪一種,最後都是吐完整 HTML 含特定內容給 Client,自然能解決 SEO 的問題。

聽起來蠻不錯的,那 Gatsby.js 呢?
要提開發者選擇 Next.js 原因,似乎都要順便提 Gatsby.js。

Gatsby.js

最常和 Next.js 相比較的就是 Gatsby.js。它也是 React 官方推薦的 Toolchain 之一。

搜尋網路文章,「通常」會說 Gatsby.js 是 static site generator,尤其是和 Next.js 比較的時候。其實這等同上面 Next.js 提到的 SSG,要注意 Next.js 9.3 版後才推出 SSG,舊一點的文章可能和現今有所差異。

若還是不理解什麼是 static site generator,可以看看 Gatsby.js 官網的說明:what is a static site generator

既然都是 static site 了,在 LCP 和 SEO 當然也比 CRA 開發的 Web 好。有用過 Hexo 架部落格的人大概可以理解,static site generator 在 build 好產生一個一個 HTML 後,其網頁 HTML 可以被 CDN 快取住,所以使用者載入體驗會比較好。

而這裡的 「通常」一說,細究下去可能讓人有點誤會.....,因為一進入 Gatsby.js 官網可以看到斗大的標題:

The static dynamic site generator for dynamic web developers

「通常」? static site generator?

dynamic 一詞可能讓人有點誤會。

鑒於 Gatsby.js 是在 build time 時先產好 HTML,在 client side 透過 hydration 注入初始狀態,透過 React.js 來更新網頁後續的操作,所以可以做到更多事情,這裡的 dynamic 更像是在說資料的流動嗎?Gatsby.js 不是 static site generator (SSG) 嗎?

這裡可見 Gatsby.js 官網的說明: Our site uses a lot of dynamic content and components. Isn’t Gatsby a static site generator?,再配上 Gatsby 官方影片可能會更有感覺。

所以,Gatsby.js 是 SSG,但它能做更多。隨著 Client Javascript 的加強,「Static is the new Dynamic」,個人認為官網標題若真要把 static 一詞劃掉,不如改成 React application for web developers 甚至 The jamstack markup for web developers 會比較中性一點。

這種非 SPA 架構,而是由 static HTML + Client 端 Javascript (React/Vue/Angular) 組成技術,又稱為 Jamstack,關於 Jamstack 架構是什麼,可以參考這篇我的另一篇筆記淺談 Jamstack 架構

Next.js 與 Gatsby.js 的差異

  • 同樣是 React Toolchain,Gatsby 是 SSG 靜態網站產生器,Next 按需求可選 SSG、SSR 甚至是 ISR。
  • Gatsby 在 build time 時產生前端檔案。
  • Next SSG 模式在 build time 時產生前端檔案,SSR 模式在專案 Server runtime 時動態產生前端檔案。
  • 在同是 SSG 的 jamstack 架構下比較,Gatsby 速度與效能都略勝 Next SSG 模式 (見 Gatsby 官方比較表)。
  • Next 比 Gatsby 在 HTML 的構成上更有彈性 (SSG、 SSR 與 ISR)。
  • Gatsby 是 serverless rendering generates。
  • Next SSG 可以如 Gatsby 只輸出 static HTML,純論這個情境也是 serverless rendering generates。
  • 但 Next 還可以做到更多,因其包含了 Node.js 做 Server,在 SSR 與 ISR 的情境下(SSR 與 ISR 需要 Server run time 才能運作),這個情境下就不是 serverless 了,反而說 Next.js 是一個全端 Framework 會比較恰當。

React 18 未來會有 SSR 調整,還可以碰 Next.js 和 Gatsby.js 嗎?

我認為可以。

過去前端世界的問題之一是隨著新技術的出現,開發者會不斷地改變遵循的框架或想法。並不斷為新想法構建新框架,然後將舊框架拋在後面。

這種想法其實不符合社群生態,因為可以先想想如何將新想法或技術融進既有的框架裡,現在前端社群的發展已趨近成熟,技術發展已漸進式演變而非直接取代,而 Next.js 和 Gatsby.js 同為 React 官方推薦的 Toolchain,不管未來是否有新的 Toolchain 冒出來,我相信 Next.js 和 Gatsbyjs 在 React 18 版正式發布後,一定會有對應功能的更新,因此不用怕未來乏人問津或沒落。

總結

鑒於 Next.js 不僅能做到 Gatsby.js 的 SSG 模式,並且還有其他模式可供開發者斟酌使用,我私人會偏好 Next.js 多一點。

若今天在公司上班,遇到要開新 React 專案的需求,有授權給我開的前提下:

  • 後台專案,選擇 CRA。
  • 少量獨立活動網站,斟酌選 Express + jQuery + pug,或者 Gatsby.js 與 Next.js 挑其一。
  • 中等數量或複雜活動網站、前台專案,我選 Next.js。
  • 潛在需要 SEO 新專案、新創 MVP 產品,我會選擇 Next.js,至少確保未來有 SSG 與 SSR 的彈性。

總之遇到需求時先想想專案性質,技術架構的選擇沒有優劣,CRA、Gatsby 與 Next 都很優秀,只是使用的場域和要解決的需求不同而已。本身剛入 Next 坑不久,最近才用它實做完一個前台外包案,歡迎大家一起交流討論。

參考

Nextjs 官方文件
Gatsby 官方文件
Next.js 開發心得全記錄
Next.JS vs. Gatsby.JS Frameworks- All You Need To Know
New to Jamstack? Everything You Need to Know to Get Started
Client-Side Rendering vs Server-Side Rendering vs Static Site Generation
[Day 15] Server-Side-Rendering - (2) feat. Next.js
Incremental Static Regeneration
A Complete Guide To Incremental Static Regeneration (ISR) With Next.js
How To Connect MySQL And Auth To A Next.js App


#Next.js #React #gatsby #ssr #ssg #Prerender









Related Posts

效能

效能

菜比八寫後端(5) - TypeORM簡介

菜比八寫後端(5) - TypeORM簡介

簡明 Scratch 小遊戲開發入門教學

簡明 Scratch 小遊戲開發入門教學


Comments