CSR 的 SEO 危機與 SSR 復興#
2015 年前後,單頁應用(SPA)似乎是 Web 唯一的未來。React、Angular、Vue 讓開發者用 JavaScript 在瀏覽器裡繪製一切:路由是客戶端的,資料是 AJAX 拉的,HTML 檔案幾乎是空的——只有一個 <div id="root"> 和一大包 JS。這個架構讓應用流暢如原生軟體,卻在三個地方撞上了牆。
第一堵牆是搜尋引擎。Googlebot 對 SPA 的處理方式是:先抓 HTML,發現裡面沒內容,再把頁面丟進渲染佇列,用無頭瀏覽器執行 JavaScript,幾天後才回來更新索引。對電商與內容網站而言,這是災難:爬蟲執行 JS 的成本高昂、排程不可控、任何一個 JS 錯誤都可能讓整頁在爬蟲眼中化為空白。搜尋排名直接等於營收,工程師們開始在部落格上哀號:「我的 React 網站 Google 看不見。」更別提 Twitter 當年退出 Open Graph 的老問題——社群分享預覽(OG tags)由爬蟲直接抓取、完全不執行 JS,SPA 的分享卡片經常是一片空白。
第二堵牆是硬體。SPA 興起的年代,矽谷工程師的手上是最新款的 MacBook;但全球多數使用者拿著一千多元人民幣的 Android 手機,在 3G 網路下載入兩百 KB 的框架、再等數百 KB 的資料。低端手機上,JavaScript 的解析與執行以「秒」計價,白屏時間成為電商轉換率的直接殺手。Google 推出 Web Vitals 指標(LCP、FID、CLS)後,效能從「工程師的潔癖」變成產品經理的 KPI。
第三堵牆是哲學的:Web 的本質是文件與連結,SPA 把它變成了必須下載整個執行環境的應用程式。
反彈從兩條路線同時開始。第一條是靜態站點生成(SSG):Kyle Mathews 的 Gatsby(2015 年開始、2017 年大成)用 React 預先在建置時渲染出所有 HTML,把 React 的開發體驗和純靜態檔案的效能結合,配合 GraphQL 資料層風靡一時的「JAMstack」運動——Netlify 的 Mathias Biilmann 在 2016 年為它命名。第二條路線走得更深:2016 年 10 月,德國裔工程師 Guillermo Rauch 的公司 Zeit(後改名 Vercel)發布了 Next.js,由 Tim Neutkens 等人接手核心開發。它的承諾簡單而致命:「為 React 提供開箱即用的伺服器端渲染、檔案系統路由與代碼分割。」開發者不必再手動配置 Webpack、Babel 與 Express 的膠水代碼,寫一個 pages/index.js,伺服器與瀏覽器各渲染一次。
這就是「同構應用」(isomorphic / universal JavaScript)思潮的產品化:同一份組件代碼,在伺服器上先渲染出完整 HTML(解決 SEO 與首屏),在瀏覽器上再「水合」(hydration)——框架把 JS 送下來,比對伺服器送來的 DOM,把事件處理器重新掛回去,讓頁面「活」起來。這個設計優雅卻昂貴:伺服器算一遍、瀏覽器再算一遍,HTML 送了、JS 還得整套送,批評者稱之為「雙重工作」,"hydration is a hack" 成為當年鐘擺擺動時最响亮的批判聲。
但市場不等人。Next.js 以驚人的迭代速度吃下 React 生態的框架位:2018 年支援靜態生成,2020 年支援增量靜態再生(ISR),2022 年 React 18 推出 Streaming SSR——伺服器把 HTML 分塊串流送出,包裹在 <Suspense> 中的慢資料區塊可以「晚點到」,先送出骨架與關鍵內容,水合也可以選擇性進行。效能指標從此有了新的施展空間。
商業上,這是開源商業模式的教科書案例:Rauch 拒絕了單靠諮詢的老路,把 Next.js 開源、把部署做成產品——Vercel 以「前端雲」的定位成長為估值十億美元級的獨角獸。框架免費、體驗閉環、部署收費,開源成為獲客的引擎而非成本。
回望這段歷史,最耐人尋味的是它的地理諷刺:阿姆斯特丹——GraphQL 誕生、React Europe 舉辦、SPA 運動最狂熱的城市——的會場上,十年前人們宣告「伺服器渲染已死」;十年後,同樣的社群歡迎 SSR 的全面復辟。架構的鐘擺從不中立地停在某一端,它只是忠實地回應每一代物理約束(網路、裝置、爬蟲)與商業壓力(SEO、轉換率)。SPA 殺死 SSR,SSR 殺回來了——而當我們把鏡頭拉遠,這只是 Web 架構三十年鐘擺中最近的一次盪幅。下一次盪動,將由「組件究竟該在哪裡執行」的追問啟動,那正是伺服器組件的故事。