前端同構渲染的相關架構,給我最直觀的感覺,這是前端渲染最爲複雜的一種方案,也是爲了追求極致的用戶體驗不得不去作的一種嘗試,雖然 Node.js 的引入賦能了傳統前端領域、SEO 優化也再也不是個問題,但很明顯,這些只是副產品。html
上帝爲了咱們開了一扇窗,同時也會爲咱們關上一扇門。前端
咱們所知的傳統型 SPA,單頁面應用,貼近用戶端越近,交互越複雜,它的弊端就越明顯,在咱們享受 JavaScirpt 給咱們帶來的無刷新體驗和組件化帶來的開發效率的同時,『白屏』這個隨着 SPA 各類優勢隨之而來的缺點被遺忘,咱們擁有菊花方案在 JavaScript 沒有將 DOM 構建好以前蒙層,擁有白屏監控方案將真實用戶數據上報改進,但並無觸碰到白屏問題的本質,那就是『DOM 的構建者是 JavaScript,而非原生的瀏覽器』。vue
<html> <head><title /></head> <body> <div id="root"></div> <script src="render.js"></script> </body> </html> 複製代碼
如上代碼,在 SPA 架構中,服務器端直接給出形如這樣的 HTML,瀏覽器在渲染 body#root 這個節點完成以後,頁面的繪製區域其實仍是空的,直到 render.js 構建好真實的 DOM 結構以後再 append 到 #root上去。此時,首屏展現出來時,必然是 render.js 經過網絡請求完畢,而後加上 JavaScript 執行完成以後的。node
讓咱們回到最初的那個前端時代,那時候 JavaScript 尚未那麼強大,咱們的服務器端所有吐出 HTML 給前端,咱們使用 jQuery 解決用戶的交互,這種方式雖有不少弊病,但不能否認的是擁有理論上最低白屏時間。webpack
<html> <head><title /></head> <body> <div id="root"> <div class="header"> <img src="logo.png" /> </div> <div calss="content"> <div class="shopitem"> </div> </div> </div> </body> </html> 複製代碼
如上代碼,在直出的服務器渲染中,瀏覽器直接拿到最終的 HTML,瀏覽器經過解析 HTML 以後將 DOM 元素生成而進行渲染。因此相比於 SPA,服務器端渲染從直觀上看:ios
**web
Node.js 的出現極大程度的給傳統前端賦予了更大的能量,前端的分離也從前期的物理文件的區分轉變爲職責上的區分,前端開發者從頁面仔的噩夢中解脫出來,最重要的是,JavaScript 能在服務器端執行了。在享受這些紅利的同時,咱們就會不自覺的設想一種方案,它擁有 SPA 的大部分優勢,卻解決了它大部分的缺點,那就是服務器端輸出 HTML,而後由客戶端複用該 HTML,繼續 SPA 模式,這樣豈不是既解決了白屏和 SEO 問題,又繼承了無刷新的用戶體驗和開發的組件化嘛。redis
嗯,若是這樣的話,就會有個一致性的問題。咱們必須在瀏覽器端複用服務器端輸出的 HTML 才能避免多套代碼的適配,而傳統的模板渲染是可行的,只要選擇一套同時支持瀏覽器和 Node.js 的模板引擎就能搞定。咱們寫好模板, 在 Node.js 準備好數據,而後將數據灌入模板產出 HTML,輸出到瀏覽器以後由客戶端 JavaScript 承載交互,搞定。axios
軟件開發中遇到的全部問題,均可以經過增長一層抽象而得以解決後端
思路到了這裏,咱們就會發現,『模板』實際上是一種抽象層,雖然底層的 HTML 只能跑在瀏覽器端,可是頂層的模板卻能經過模板引擎同時跑在瀏覽器和服務器端,此爲垂直方向,在水平方向上,模板將數據和結構解耦,將數據灌入結構,這種灌入,實際是一錘子買賣,管生無論養。
隨着時間的推動,組件化的大潮來了,其核心概念 Virtual DOM 依其聲明式和高性能讓前端開發者大呼爽爽爽,但究其本質,就是爲了解決頻繁操做 DOM 而在 HTML 之上作的一層抽象,與模板不一樣的是,它將數據與結構產生交互,有表明的要數 Facebook 方使用的單項數據流和 Vue 方使用的 MVVM 數據流,大道至簡,咱們觀察函數 UI = F(data), 其中 UI 爲最終產出前端界面,data 爲數據,F 則爲模板結構或者 Virtual DOM,模板的方式是 F 只執行一遍,而組件方式則爲每次 data 改變都會再執行一遍。
因此理論上,不管是模板方式仍是組件方式,先後端同構的方案都呼之欲出,咱們在 Node.js 端獲取數據 ,執行 F 函數,獲得 HTML輸出給瀏覽器,瀏覽器 JavaScript 複用 HTML,繼續執行 F 函數,等到數據變化,繼續執行 F 函數,交互也獲得解決,完美~~~
但因爲組件化大勢所趨,下文將略去模板方案,咱們以 Vue 爲類比,下圖代表其實施思路:
因爲 F 同時須要在瀏覽器端和服務器端執行,因此對於整個 Vue App,咱們須要同時支持兩端,也就是通用代碼。因此咱們須要將 SPA 架構的代碼進行改造:
if (process.env.EXEC_ENV === 'client') { window.addEventListener(...); } if (process.env.EXEC_ENV === 'server') { } 複製代碼
至此,白屏問題問題看起來是解決了,經過把 JavaScript 的渲染邏輯放到 Node.js 端進行,咱們加快了首屏出現的時間,可是聯想到 Node.js 對前端的賦能,咱們或許能夠作的更多。
讓咱們把視角移動的更細緻一些,關注『從服務器端輸出 HTML』這一部分,其隱藏的含義是咱們須要把 App 渲染的全部 HTML 都輸出給前端,其實否則,舉個栗子:
好比在移動端有一個頁面,它有大約 10 屏的高度,若是咱們在服務器端所有輸出 10 屏實際上是有點浪費的,咱們能夠只輸出首屏須要的,從而下降 render 執行時間從而下降 TTFB 時間,讓頁面更快的到達用戶眼前。實踐中,通常狀況是輸出大概快兩屏的樣子,就能處理因此機型的高度問題,剩下的 8 屏,在瀏覽器端繼續渲染,漸進產出內容,用戶無感知。
得益於 Node.js 輸出 HTML 的另外一層含義,就是咱們能夠直接在首次接觸就能感知到客戶端,也就有了足夠的靈活性,再舉個栗子:
有個針對安卓平臺和 iOS 平臺不一樣的腳本只要加載,若是在 SPA 狀況下,只有等 JavaScript 執行時咱們斷定 navigation.userAgent 來獲知先在是哪一個平臺,而後在 appendChild 一個 script 到 body,但若是服務端能首次接觸就能感知,咱們能夠在服務端直接拿到 HTTP 請求中的 userAgent 斷定平臺,根據標識在模板中處理,很顯然,這樣很穩。
另外,若是有一些特別複雜的計算,服務端能夠有更多的辦法將數據更快的處理,以免繁忙無比的瀏覽器接手。
通常的業務場景下,咱們須要在 Node.js 中經過內網將數據獲取到,而後經過 render 函數渲染出 HTML(通常須要將數據附帶給 HTML 輸出以便重複利用),這個時候咱們能夠經過頁面訪問地址和生成的 HTML 字符串作緩存策略,在緩存(通常選擇 redis 等方案)以後,下次直接將一樣的頁面直接輸出到前端,可大幅提升渲染性能。
但這種方案也有不少限制,由於要考慮頁面地址、多平臺下、帳戶是否登陸,頁面是否須要改動等狀況:
同構渲染看似美好,但其相對傳統 SPA 確有着更多挑戰:
服務器端渲染相對應傳統的 Node.js 應用,renderToString 函數不只 CPU 密集,並且不一樣的組件對機器資源的要求不盡相同,這就更須要 Node.js 指標的監控、日誌的記錄、錯誤的收集、崩潰機制的完善。這裏額外的關鍵的指標是 renderToString 的時間,它反應了 Node.js 渲染所使用的時間,若是加入緩存機制,就須要統計命中率等等。
關於寫通用代碼,要求比 SPA 架構對開發者提出了更高的要求,咱們須要當心再當心,由於萬一搞錯,將致使很難排查的內存泄露和 CPU 飆升,而且一旦出了問題,就像要修理天上跑的飛機同樣,很是困難。還記得有一次在相似 componentWillMount 寫了一些跟瀏覽器相關的代碼致使的內存飆升,還有一次 JSON.stringify 一個大對象致使的 CPU 飆升,不堪回首。這方面 alinode 作的很好,確實能夠知足這種飛機場景。
爲了效率, 前端們付出了艱辛的努力,不管是工程上咱們想方設法的製造工具,仍是組件化的引入,咱們解決的是開發的效率,而不管是 Virtual DOM 的引入解決頻繁操做的 DOM,仍是用了提高用戶體驗而使用的 SPA 架構,咱們解決的是用戶的使用效率,是前端的性能。而同構渲染也是這樣一種方案,它引入了 Node.js 的複雜度,要求咱們寫出限制更多的代碼,其根本目的仍是爲了讓用戶更快更早的看到頁面,那怕是 50 毫秒,那怕是 10 毫秒。
關於咱們
咱們是螞蟻保險體驗技術團隊,來自螞蟻金服保險事業羣。咱們是一個年輕的團隊(沒有歷史技術棧包袱),目前平均年齡92年(去除一個最高分8x年-團隊leader,去除一個最低分97年-實習小老弟)。咱們支持了阿里集團幾乎全部的保險業務。18年咱們產出的相互寶轟動保險界,19年咱們更有多個重量級項目籌備動員中。現伴隨着事業羣的高速發展,團隊也在迅速擴張,歡迎各位前端高手加入咱們~
咱們但願你是:技術上基礎紮實、某領域深刻(Node/互動營銷/數據可視化等);學習上善於沉澱、持續學習;性格上樂觀開朗、活潑外向。
若有興趣加入咱們,歡迎發送簡歷至郵箱:luguang.ylg@antfin.com
本文做者:螞蟻保險-體驗技術組-月影
掘金地址:楊柳岸醬