嗨~ 歡迎閱讀第 46 期的 ExplainThis 全端雙週報。在進入正式內容前,先跟讀者們說聲新年快樂!希望讀者們在年假期間,都有好好的休息、好好充電,年後可以持續在職涯中成長。
在這邊與讀者們預告,ExplainThis 經營的 E+ 成長計畫在年後預計推出今年的第一門線上課程,主題會是《軟體工程師求職全攻略》。這堂課會收錄過去我們協助讀者們求職的精華重點,從如何寫履歷,到如何準備行為、技術面試,以及在過程中遇到挫折時如何調整心態。
如果有預計年後要轉職的讀者,歡迎加入E+ ,除了線上課程,在求職中有遇到任何問題,也都可以在 E+ 的 Discord 社群中討論,另外我們也有履歷健檢的頻道,讓讀者們問到飽 (E+ 的詳細介紹可以點這邊)。
以上,接著讓我們進到這週的主題文吧。
前端渲染模式選擇
在現代前端開發中,渲染模式的選擇,幾乎是每個新專案最開始必須要面對問題。因此,前端工程師需要知道有哪些不同的渲染模式、以及需要知道在什麼情境該用哪一種渲染模式。
客戶端渲染 client-side rendering (CSR)
從網頁歷史來看,在早期前後端沒有特別分離的狀況下,都是由後端渲染,然後由後端輸出到前端後,前端再搭配 JavaScript 負責一些簡單互動與動畫。不過,隨著瀏覽器越來越強大,變得有能力在瀏覽器上做複雜運算與渲染;與此同時,在客戶端渲染能帶來更佳的使用者體驗,也讓業界開始發展客戶端渲染 (client-side rendering,接下來簡稱 CSR)。
在 CSR 模式下的渲染流程 在 CSR 模式下,請求發送後,會先拿到一個空白的 HTML,做為最基本的骨架
<html>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>
接著會根據 <script>
標籤來開始下載 JavaScript (上面的例子會是下載 bundle.js
),然後用 JavaScript 來去渲染,包含由 JavaScript 來產出 DOM,並完成 關鍵渲染路徑 (critical rendering path) 一文談到的渲染流程。渲染後,才會根據客戶端寫的程式碼,呼叫後端 API 來拉取資料,拉完資料呈現完主要內容,才會上報 Web Vitals 的 LCP。
CSR 的優點
CSR 之所以會出現,是因為上面提到這種傳統的「由後端把東西都準備好後送到前端」渲染模式,因為會有整個頁面換頁的狀況,會導致使用者體驗不那麼理想。不過,在 CSR 的模式下,每當有任何與後端的互動 (例如點擊按鈕送出表單),不需用由伺服器端送新的一頁,所以不會有因為換頁造成的不佳使用者體驗。
除此之外,CSR 把前後端的程式碼切的很清楚,不像傳統會把程式碼都寫在一起;在可維護性上,CSR 因相對好維護。
CSR 的問題
雖然 CSR 可以帶給使用者更好的使用體驗,但也有很顯而易見的問題:
剛進網站的等待時間比較久:CSR 模式下,使用者進到網站後,一開始會是全白或骨架 (skeleton) 的畫面,必須等到 JavaScript 下載完、解析完、執行完、更新完 DOM,才會有網站真正的畫面。當 CSR 的建構時的打包產物 (bundle) 越大包,要花越多時間下載與解析,也會等越久。這段等待時間的使用者體驗不是那麼好。
SEO 比較沒那麼好:由於 CSR 模式下,一開始伺服器端只會送給客戶端一個空白 HTML,搜尋引擎的爬蟲也只會爬到空白的 HTML,所以在爬蟲方面爬的結果通常是空白的。即使現在許多搜尋引擎會在爬蟲時執行 JavaScript,但不保證該搜尋引擎的 JavaScript 執行方式如預期,所以無法保證 SEO 是否良好。
伺服器端渲染 Server-side rendering
在談完 CSR 後,接著讓我們來談 SSR,也就是伺服器端渲染 (Server-side rendering)。由於 SSR 的具體實作細節,在過去有不小的變化,在這邊我們會拆成不同的 SSR 來討論。
傳統的 SSR
如上面提到,在早些年基本上沒有什麼前後端分離、沒有 CSR 的存在,網頁應用基本上都是在伺服器端完成所有處理,包含像資料庫拿資料、把頁面渲染好,然後傳給客戶端。
傳統 SSR 的好處
比起 CSR,傳統的 SSR 不用把整包的 JavaScript 傳到客戶端,而是把渲染好的 HTML 傳給前端,這樣的好處是,如果客戶端是用非高階裝置、非高速網路,也不用擔心 JavaScript 太大一包要下載與解析很久。與此同時,因為是直接送給前端渲染好的 HTML,是有內容的,所以 SEO 的問題也不用擔心。
傳統 SSR 的問題
雖然傳統 SSR 有其好處,但 CSR 會出現也不是沒道理的,當時之所以 CSR 會盛行,是因為傳統的 SSR 每次有操作時 (例如送出表單後) 整個頁面要重新加載,導致有換頁的體驗,這樣的使用者體驗不是太好。
SSR + 注水 (hydration)
這時你可能會問,有沒有什麼方法,是可以有傳統 SSR 先渲染好 HTML 的優點,又同時可以像 CSR 這樣後續操作都由客戶端接手,可以提供比較好的使用體驗? 事實上近代開發者用的 SSR 正是用這樣的方式。
如果用 Next.js 這種框架的 SSR,會是有別於傳統的 SSR,這種作法一樣是先把 HTML 在伺服器端渲染好;不過不同的地方在於,傳到前端後,會有一個注水 (hydration),然後讓後續的操作變得不需用換頁,帶來更好的使用體驗。
具體來說,流程會是這樣進行:
在伺服器完成渲染後,將完整的 HTML 傳到客戶端,這時客戶端已經有完整的網頁內容可以看到。
然後客戶端下載少量的 JavaScript,在客戶端做事件綁定 (就是上面提到的注水 hydration),因為初始畫面已經有了,注水只需用依照已經有的 DOM 來綁定事件處理器即可。
注水完成後,就會跟 CSR 一樣,由客戶端接管,使用者會有良好的互動體驗,而不會像傳統的 SSR 這樣需要整個換頁。
SSR + 注水 的好處
解決了傳統 SSR 的互動性問題,同時解決了 CSR 在前面有一大段空白的問題。
SSR + 注水 的問題
雖然這方式看似解決前兩種方式的問題,但仍有自己的問題。具體來說,雖然在 HTML 送達客戶端後就立即能展示畫面,不會有空白的那一段;但在有 HTML 到完成 JavaScript 注水這段期間,會是看得到畫面,但沒辦法有互動。相信讀者們過去或多或少遇過,網站有畫面,但怎麼點按鈕都沒用,這種不好的使用體驗,是 SSR + 注水的主要問題。
閱讀更多 關於前端渲染模式,還有許多能夠更深、更廣探討的。舉例來說,前面談到 CSR 的問題是有解決的辦法,後面談到的 SSR 還有進一步的串流式 SSR,以及不同改良版的注水方式。
如果想閱讀更多前端渲染模式主題的內容,歡迎加入 E+ 成長計畫,我們在 E+ 的內容有更多相關的討論 (連結)。
本期推薦
DeepSeek 是過去兩週在社群上最多人討論的話題,在社群上可以看到不少人擔心資料安全的疑慮,推薦一讀 Perplexity 執行長寫的這個解釋(連結)。至於要如何在本地跑開源的 LLM 模型,可以參考這篇貼文 (連結)
抽象化是對軟體工程師來說,很重要的能力之一,《The magic of keeping one level of abstraction per function》 一文用具體的例子,來談透過抽象化如何讓程式碼更好讀好維護(連結)
上期雙週報分享 Shopify 團隊當年選擇轉向 React Native 這個技術棧的文章,這週看到 Shopify 前 CTO 進一步分享當年做這個技術決策的心路歷程。想要培養做技術決策的能力,這篇很直得一讀 (連結)
Linear 的創辦人分享了一篇再談「品質」的文章,看了特別有感。近年來軟體開發很看重 MVP,先有在迭代,高品質的軟體產品反而變得稀缺,這點確實可惜 (連結)
最近社群有一個基於 Next.js 開源的全端框架 JStack,把很多東西都包好了,例如全端的型別校驗、各大部署平台串接。推薦有興趣的人可以用 JStack 來做個人專案 (連結)
談到全端的型別校驗,Standard Schema 是近期開源社群討論度非常高的 schema 工具,讓全端的型別溝通能夠用更容易做到。目前已經有很多套件採用(連結)
這週 MDN 分享了 JavaScript 的 Temporal 近期已經有逐漸進到各大瀏覽器的實驗版本。Temporal 解決過去用 Date 物件會遇到的時區、解析等問題。在未來穩定後,開發者就不再需用 Moment.js 或 date-fns 這類的套件了 (連結)
近年來最熱門的CSS 工具 Tailwind 在上週推出了 4.0 版本,除了在效能上有非常大的優化外,在各方面也都提供更完整的開發體驗 (連結)
文末彩蛋
感謝讀到文末的你,這邊分享這週讀到一句很讓人感到深刻的話。如果在排優先順序時,什麼事都標注最重要,那就意味著沒有哪件事是真的比其他是重要的。
If you have more than three or four P0 big project initiatives in a quarter, you don't have any P0s. You have a bunch of P1s and P2s.
— Jaana Dogan
:)