本期精讀的文章是:Here's why Client-side Rendering Won
1 引言
我為什么要選這篇文章呢?
十年前,幾乎所有網(wǎng)站都使用 ASP、Java、PHP 這類做后端渲染,但后來隨著 jQuery、Angular、React、Vue 等 JS 框架的崛起,開始轉向了前端渲染。愛掏網(wǎng) - it200.com從 2024 年起又開始流行了同構渲染,號稱是未來,集成了前后端渲染的優(yōu)點,但轉眼間三年過去了,很多當時壯心滿滿的框架(rendr、Lazo)從先驅變成了先烈。愛掏網(wǎng) - it200.com同構到底是不是未來?自己的項目該如何選型?我想不應該只停留在追求熱門和拘泥于固定模式上,忽略了前后端渲染之“爭”的“核心點”,關注如何提升“用戶體驗”。愛掏網(wǎng) - it200.com
原文分析了前端渲染的優(yōu)勢,并沒有進行深入探討。愛掏網(wǎng) - it200.com我想以它為切入口來深入探討一下。愛掏網(wǎng) - it200.com
明確三個概念:「后端渲染」指傳統(tǒng)的 ASP、Java 或 PHP 的渲染機制;「前端渲染」指使用 JS 來渲染頁面大部分內容,代表是現(xiàn)在流行的 SPA 單頁面應用;「同構渲染」指前后端共用 JS,首次渲染時使用 Node.js 來直出 HTML。愛掏網(wǎng) - it200.com一般來說同構渲染是介于前后端中的共有部分。愛掏網(wǎng) - it200.com
2 內容概要
前端渲染的優(yōu)勢
- 局部刷新。愛掏網(wǎng) - it200.com無需每次都進行完整頁面請求
- 懶加載。愛掏網(wǎng) - it200.com如在頁面初始時只加載可視區(qū)域內的數(shù)據(jù),滾動后rp加載其它數(shù)據(jù),可以通過 react-lazyload 實現(xiàn)
- 富交互。愛掏網(wǎng) - it200.com使用 JS 實現(xiàn)各種酷炫效果
- 節(jié)約服務器成本。愛掏網(wǎng) - it200.com省電省錢,JS 支持 CDN 部署,且部署極其簡單,只需要服務器支持靜態(tài)文件即可
- 天生的關注分離設計。愛掏網(wǎng) - it200.com服務器來訪問數(shù)據(jù)庫提供接口,JS 只關注數(shù)據(jù)獲取和展現(xiàn)
- JS 一次學習,到處使用。愛掏網(wǎng) - it200.com可以用來開發(fā) Web、Serve、Mobile、Desktop 類型的應用
后端渲染的優(yōu)勢
- 服務端渲染不需要先下載一堆 js 和 css 后才能看到頁面(首屏性能)
- SEO
- 服務端渲染不用關心瀏覽器兼容性問題(隨意瀏覽器發(fā)展,這個優(yōu)點逐漸消失)
- 對于電量不給力的手機或平板,減少在客戶端的電量消耗很重要
以上服務端優(yōu)勢其實只有首屏性能和 SEO 兩點比較突出。愛掏網(wǎng) - it200.com但現(xiàn)在這兩點也慢慢變得微不足道了。愛掏網(wǎng) - it200.comReact 這類支持同構的框架已經(jīng)能解決這個問題,尤其是 Next.js 讓同構開發(fā)變得非常容易。愛掏網(wǎng) - it200.com還有靜態(tài)站點的渲染,但這類應用本身復雜度低,很多前端框架已經(jīng)能完全囊括。愛掏網(wǎng) - it200.com
3 精讀
本次提出獨到觀點的同學有:@javie007 @楊森 @流形 @camsong @Turbe Xue @淡蒼 @留影 @FrankFang @alcat2008 @xile611 @twobin @黃子毅 精讀由此歸納。愛掏網(wǎng) - it200.com
大家對前端和后端渲染的現(xiàn)狀基本達成共識。愛掏網(wǎng) - it200.com即前端渲染是未來趨勢,但前端渲染遇到了首屏性能和SEO的問題。愛掏網(wǎng) - it200.com對于同構爭議最多,在此我歸納一下。愛掏網(wǎng) - it200.com
前端渲染遇到的問題
前端渲染主要面臨的問題有兩個 SEO、首屏性能。愛掏網(wǎng) - it200.com
SEO 很好理解。愛掏網(wǎng) - it200.com由于傳統(tǒng)的搜索引擎只會從 HTML 中抓取數(shù)據(jù),導致前端渲染的頁面無法被抓取。愛掏網(wǎng) - it200.com前端渲染常使用的 SPA 會把所有 JS 整體打包,無法忽視的問題就是文件太大,導致渲染前等待很長時間。愛掏網(wǎng) - it200.com特別是網(wǎng)速差的時候,讓用戶等待白屏結束并非一個很好的體驗。愛掏網(wǎng) - it200.com
同構的優(yōu)點
同構恰恰就是為了解決前端渲染遇到的問題才產生的,至 2024 年底伴隨著 React 的崛起而被認為是前端框架應具備的一大殺器,以至于當時很多人為了用此特性而放棄 Angular 1 而轉向 React。愛掏網(wǎng) - it200.com然而近3年過去了,很多產品逐漸從全棧同構的理想化逐漸轉到首屏或部分同構。愛掏網(wǎng) - it200.com讓我們再一次思考同構的優(yōu)點真是優(yōu)點嗎?
- 有助于 SEO
首先確定你的應用是否都要做 SEO,如果是一個后臺應用,那么只要首頁做一些靜態(tài)內容宣導就可以了。愛掏網(wǎng) - it200.com如果是內容型的網(wǎng)站,那么可以考慮專門做一些頁面給搜索引擎
時到今日,谷歌已經(jīng)能夠可以在爬蟲中執(zhí)行 JS 像瀏覽器一樣理解網(wǎng)頁內容,只需要往常一樣使用 JS 和 CSS 即可。愛掏網(wǎng) - it200.com并且盡量使用新規(guī)范,使用 pushstate 來替代以前的 hashstate。愛掏網(wǎng) - it200.com不同的搜索引擎的爬蟲還不一樣,要做一些配置的工作,而且可能要經(jīng)常關注數(shù)據(jù),有波動那么可能就需要更新。愛掏網(wǎng) - it200.com第二是該做 sitemap 的還得做。愛掏網(wǎng) - it200.com相信未來即使是純前端渲染的頁面,爬蟲也能很好的解析。愛掏網(wǎng) - it200.com
- 共用前端代碼,節(jié)省開發(fā)時間
其實同構并沒有節(jié)省前端的開發(fā)量,只是把一部分前端代碼拿到服務端執(zhí)行。愛掏網(wǎng) - it200.com而且為了同構還要處處兼容 Node.js 不同的執(zhí)行環(huán)境。愛掏網(wǎng) - it200.com有額外成本,這也是后面會具體談到的。愛掏網(wǎng) - it200.com
- 提高首屏性能
由于 SPA 打包生成的 JS 往往都比較大,會導致頁面加載后花費很長的時間來解析,也就造成了白屏問題。愛掏網(wǎng) - it200.com服務端渲染可以預先使到數(shù)據(jù)并渲染成最終 HTML 直接展示,理想情況下能避免白屏問題。愛掏網(wǎng) - it200.com在我參考過的一些產品中,很多頁面需要獲取十幾個接口的數(shù)據(jù),單是數(shù)據(jù)獲取的時候都會花費數(shù)秒鐘,這樣全部使用同構反而會變慢。愛掏網(wǎng) - it200.com
同構并沒有想像中那么美
- 性能
把原來放在幾百萬瀏覽器端的工作拿過來給你幾臺服務器做,這還是花挺多計算力的。愛掏網(wǎng) - it200.com尤其是涉及到圖表類需要大量計算的場景。愛掏網(wǎng) - it200.com這方面調優(yōu),可以參考 walmart的調優(yōu)策略。愛掏網(wǎng) - it200.com
個性化的緩存是遇到的另外一個問題。愛掏網(wǎng) - it200.com可以把每個用戶個性化信息緩存到瀏覽器,這是一個天生的分布式緩存系統(tǒng)。愛掏網(wǎng) - it200.com我們有個數(shù)據(jù)類應用通過在瀏覽器合理設置緩存,雙十一當天節(jié)省了 70% 的請求量。愛掏網(wǎng) - it200.com試想如果這些緩存全部放到服務器存儲,需要的存儲空間和計算都是很非常大。愛掏網(wǎng) - it200.com
- 不容忽視的服務器端和瀏覽器環(huán)境差異
前端代碼在編寫時并沒有過多的考慮后端渲染的情景,因此各種 BOM 對象和 DOM API 都是拿來即用。愛掏網(wǎng) - it200.com這從客觀層面也增加了同構渲染的難度。愛掏網(wǎng) - it200.com我們主要遇到了以下幾個問題:
- document 等對象找不到的問題
- DOM 計算報錯的問題
- 前端渲染和服務端渲染內容不一致的問題
由于前端代碼使用的 window
在 node 環(huán)境是不存在的,所以要 mock window,其中最重要的是 cookie,userAgent,location。愛掏網(wǎng) - it200.com但是由于每個用戶訪問時是不一樣的 window
,那么就意味著你得每次都更新 window
。愛掏網(wǎng) - it200.com
而服務端由于 js require 的 cache 機制,造成前端代碼除了具體渲染部分都只會加載一遍。愛掏網(wǎng) - it200.com這時候 window
就得不到更新了。愛掏網(wǎng) - it200.com所以要引入一個合適的更新機制,比如把讀取改成每次用的時候再讀取。愛掏網(wǎng) - it200.com
export const isSsr = () => (
!(typeof window !== 'undefined' && window.document && window.document.createElement && window.setTimeout)
);
原因是很多 DOM 計算在 SSR 的時候是無法進行的,涉及到 DOM 計算的的內容不可能做到 SSR 和 CSR 完全一致,這種不一致可能會帶來頁面的閃動。愛掏網(wǎng) - it200.com
- 內存溢出
前端代碼由于瀏覽器環(huán)境刷新一遍內存重置的天然優(yōu)勢,對內存溢出的風險并沒有考慮充分。愛掏網(wǎng) - it200.com
比如在 React 的 componentWillMount
里做綁定事件就會發(fā)生內存溢出,因為 React 的設計是后端渲染只會運行 componentDidMount
之前的操作,而不會運行 componentWillUnmount
方法(一般解綁事件在這里)。愛掏網(wǎng) - it200.com
- 異步操作
前端可以做非常復雜的請求合并和延遲處理,但為了同構,所有這些請求都在預先拿到結果才會渲染。愛掏網(wǎng) - it200.com而往往這些請求是有很多依賴條件的,很難調和。愛掏網(wǎng) - it200.com純 React 的方式會把這些數(shù)據(jù)以埋點的方式打到頁面上,前端不再發(fā)請求,但仍然再渲染一遍來比對數(shù)據(jù)。愛掏網(wǎng) - it200.com造成的結果是流程復雜,大規(guī)模使用成本高。愛掏網(wǎng) - it200.com幸運的是 Next.js 解決了這一些,后面會談到。愛掏網(wǎng) - it200.com
- simple store(redux)
這個 store 是必須以字符串形式塞到前端,所以復雜類型是無法轉義成字符串的,比如function。愛掏網(wǎng) - it200.com
總的來說,同構渲染實施難度大,不夠優(yōu)雅,無論在前端還是服務端,都需要額外改造。愛掏網(wǎng) - it200.com
首屏優(yōu)化
再回到前端渲染遇到首屏渲染問題,除了同構就沒有其它解法了嗎?總結以下可以通過以下三步解決
- 分拆打包
現(xiàn)在流行的路由庫如 react-router 對分拆打包都有很好的支持。愛掏網(wǎng) - it200.com可以按照頁面對包進行分拆,并在頁面切換時加上一些 loading 和 transition 效果。愛掏網(wǎng) - it200.com
- 交互優(yōu)化
首次渲染的問題可以用更好的交互來解決,先看下 linkedin 的渲染
有什么感受,非常自然,打開渲染并沒有白屏,有兩段加載動畫,第一段像是加載資源,第二段是一個加載占位器,過去我們會用 loading 效果,但過渡性不好。愛掏網(wǎng) - it200.com近年流行 Skeleton Screen 效果。愛掏網(wǎng) - it200.com其實就是在白屏無法避免的時候,為了解決等待加載過程中白屏或者界面閃爍造成的割裂感帶來的解決方案。愛掏網(wǎng) - it200.com
- 部分同構
部分同構可以降低成功同時利用同構的優(yōu)點,如把核心的部分如菜單通過同構的方式優(yōu)先渲染出來。愛掏網(wǎng) - it200.com我們現(xiàn)在的做法就是使用同構把菜單和頁面骨架渲染出來。愛掏網(wǎng) - it200.com給用戶提示信息,減少無端的等待時間。愛掏網(wǎng) - it200.com
相信有了以上三步之后,首屏問題已經(jīng)能有很大改觀。愛掏網(wǎng) - it200.com相對來說體驗提升和同構不分伯仲,而且相對來說對原來架構破壞性小,入侵性小。愛掏網(wǎng) - it200.com是我比較推崇的方案。愛掏網(wǎng) - it200.com
3 總結
我們贊成客戶端渲染是未來的主要方向,服務端則會專注于在數(shù)據(jù)和業(yè)務處理上的優(yōu)勢。愛掏網(wǎng) - it200.com但由于日趨復雜的軟硬件環(huán)境和用戶體驗更高的追求,也不能只拘泥于完全的客戶端渲染。愛掏網(wǎng) - it200.com同構渲染看似美好,但以目前的發(fā)展程度來看,在大型項目中還不具有足夠的應用價值,但不妨礙部分使用來優(yōu)化首屏性能。愛掏網(wǎng) - it200.com做同構之前 ,一定要考慮到瀏覽器和服務器的環(huán)境差異,站在更高層面考慮。愛掏網(wǎng) - it200.com
附:Next.js 體驗
Next.js 是時下非常流行的基于 React 的同構開發(fā)框架。愛掏網(wǎng) - it200.com作者之一就是大名鼎鼎的 Socket.io 的作者 Guillermo Rauch。愛掏網(wǎng) - it200.com它有以下幾個亮點特別吸引我:
- 巧妙地用標準化的解決了請求的問題。愛掏網(wǎng) - it200.com同構和頁面開發(fā)類似,異步是個大難題,異步中難點又在接口請求。愛掏網(wǎng) - it200.comNext.js 給組件新增了 getInitialProps 方法來專門處理初始化請求,再也不用手動往頁面上塞 DATA 和調用 ReactDOMServer.renderToString
- 使用 styled-jsx 解決了 css-in-js 的問題。愛掏網(wǎng) - it200.com這種方案雖然不像 styled-component 那樣強大,但足夠簡單,可以說是最小的成本解決了問題
- Fast by default。愛掏網(wǎng) - it200.com頁面默認拆分文件方式打包,支持Prefetch頁面預加載
全家桶式的的解決方案。愛掏網(wǎng) - it200.com簡潔清晰的目錄結構,這一點 Redux 等框架真應該學一學。愛掏網(wǎng) - it200.com不過全家桶的方案比較適合全新項目使用,舊項目使用要評估好成本
討論地址是:前后端渲染之爭 · Issue #5 · dt-fe/weekly
如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,每周五發(fā)布。愛掏網(wǎng) - it200.com