最近在 Frontend 技術圈裡,Server-Side-Rendering(SSR) 可說是一個越來越火熱的概念,剛好現在工作上的架構就是使用 Next.js(React 的 SSR 框架)開發,過程中發現滿多有趣與讓自己驚豔的地方,因此想花兩個篇幅紀錄 SSR 的基本概念,還有介紹 Next.js 擁有什麼樣的特色,可以讓它成為當今 React 最熱門的 SSR 框架。
(因為本人習慣使用 React,因此以下的範例或概念都會用 React 撰寫)
在講 Server-Side-Rendering 之前,我想先談談與之相對應的概念: Client-Side-Rendering (CSR)。在前端框架(React、Vue、Angular)盛行後,SPA (Single-Page-Application) 就如雨後春筍般冒出,而它的運作概念是這樣的:
以往會被塞滿各種 element 的 HTML 檔,如今只會放入一個 tag 當作「容器」而已,例如以下範例
<html>
<head></head>
<body>
<div id="root"></div>
<script src="./script.js"></script>
</body>
</html>
上面 id 為 root 的 div 即成為了上面提到的容器,script.js 則是 react code ,採用這種方式後,頁面的元素都將交由 react 去渲染出來並塞進容器中
server 回傳只包含容器的 HTML -> 瀏覽器依據 HTML 下載 JS code -> 瀏覽器執行 React code -> 執行完畢後,頁面才完整呈現與具有互動型
採用這種方式後,我們可以不必像以前ㄧ樣準備ㄧ大堆的 HMTL 檔案,而是透過 Router 決定該渲染出哪些元素在畫面上或是該抓哪些 API data,而 routing 的過程也不再像以往ㄧ樣要重新去 server 抓取頁面造成頁面反白,使用者體驗大大的提升,這就是 Client-Side-Rendering。
看似美好,然而,它也產生了一些問題 。
SEO 對很多企業網站來說是很重要的指標,而它很大一部分得靠搜尋引擎的爬蟲爬取網站的資訊,問題出在上面說的,我們的 HTML 現在只有容器在裡面而已,其他內容都是由 JS 動態產生的,這會造成爬蟲在爬取資料時只會爬到空蕩蕩的幾個 tag…,也因此造成 SEO 分數低落。(有興趣的讀者可以在想觀察的網頁點選右鍵:檢查網頁原始碼)。
(話說 google 搜尋引擎越來越強了,未來連 CSR 頁面都能夠用新技術爬到。)
上面提到 CSR 的步驟是瀏覽器載入 JS 後再去執行它,最後靠 JS 才能渲染出要顯示的元件與交互,但是如果你的 JS 隨著專案的擴充變得非常肥大呢?瀏覽器在下載 JS 與執行 JS 上所花的時間都會因此增加,儘管現在瀏覽器已經非常快速了,仍然會對效能造成影響,而因為 CSR 是得在 JS 執行完畢時才能顯示出整個網頁,上述流程的 delay 連帶的影響到使用者等待頁面顯示的時間,據分析指出,如果網頁載入時間從 1 秒增加到 3 秒,跳出率就會增加 32%,這對於電商類型的網站來說影響營收可是很可觀的。(當然 CSR 也有 code-spliting、dynamic-import 等解決 bundle size 過於肥大的方案,但這不在本篇文章的討論範圍中。)
要解決 SEO 的問題,通常會採用 pre-rendering 的方式,所謂 pre-rendering 一般來說是指在送到 client 端以前就把 HTML 渲染出來,常見的 pre-rendering 有兩種方式:
意指 HTML 在 build time 時就由 server 產生好,並且在之後的 client request 都會共用這個 HTML。採用這種方式的優點為效能方面,因為它有較好的快取機制,不過它的限制也十分明顯,因為頁面資料的抓取(如 API call)只能透過 application build 的時候,因此比較適合使用在內容不需要經常變換的頁面上。
沒想到到這裡才要來介紹本篇的主角 SSR,SSR 與 SSG ㄧ樣都是在 server 先建立好要回傳的 HTML 內容,然而不同的是 SSR 在對該頁面的每一個 request 都會重新抓取,也就是說它是與 SSG 的 static 相對應的 dynamic 形式。每當有 client 發起對某個頁面的 request 時,server 會抓取對應的資料,建立完整的 HTML,最後再回傳給 client。
所以 SSR 理所當然可以解決 SEO 的問題,爬蟲爬到的都是從 server 建立好並帶上資訊的完整 HTML。接下來看看上面的圖,這是 SSR 的架構模型,其中與 CSR 架構最大的差別是 SSR 不必等到 JS 執行完畢後才能讓使用者看到畫面,在 server 回傳 HTML 後使用者就能夠看到頁面,即使因為 JS 還沒被執行,所以畫面還不具備互動性,但讓使用者先看到畫面,再利用人的感知延遲時間去執行 JS code,可以大大減少使用者的跳出率,於是上面提到的第二個問題也被解決了。
嗯…這次總算完美了吧….
不,SSR 的缺點(應該算吧?)是架構與邏輯會變得十分複雜,許多程式會需要分別在 server 與 client 做處理,也要特別小心不能在 server 端執行到包含如 window 等 browser 的 API,總之我認為會將專案複雜度往上提高一個層級。
除了上述提到的 SSG 與 SSR 以外,要解決爬蟲的 SEO 問題,還是有其他解決方案的,畢竟如果已經是個達到一定規模的 CSR 專案,突然要轉到 SSR 好像也滿困難的,之前實習公司的主管最近就用了蠻潮的方式(AWS Lambda)解決了 CSR 架構 pre-rendering 的問題,有興趣的讀者可以看看這篇文章。