後端渲染實踐——看掘金社區是如何實踐的

Vue.js、React.js 及 Angular.js 等等前端開發框架引入了 UI = framework(State) 的前端編程邏輯,大範圍下降了前端業務開發的難度,尤爲是面向複雜前端應用。而這些優質開源框架的普及、多端業務統1、先後端分離的需求讓愈來愈多的架構設計將大部分業務邏輯寫在了前端。css

 

可是,純前端產品也有着它的問題。上述的幾個前端框架都支持了後端渲染的功能,從而融合了先後端的問題。如何有效地整合現有前端邏輯實現後端渲染、如何優化後端渲染性能、如何實現服務器流式吐內容更快地渲染頁面的經驗,會成爲新一代 Web 開發的方向,提升前端業務開發的效率。在由七牛雲主辦的 ECUG 十週年盛會上,陰明爲你們帶來了他的實踐分享。html

 

陰明(掘金聯合創始人、CEO)前端

 


 

前端框架的繁榮及成熟

從百家爭鳴到三足鼎立

圖 1 vue

 

這是從網上找到的前端的狀態(圖 1 ),每個顏色均是某一個前端庫的分類。前端的世界就是如此,須要在一羣的選項中選擇一個,而且要跟其餘的選項 PK 。算法

 

如圖 1 所示,方框的部分寫具體的業務代碼,例如早期的 jQuery。Prototype 曾經完成了 2000 年內有複雜業務代碼的前端,寫了大量的頁面,傳統的後臺 admin 等都是這樣。再往上 Ember 比較適合業務穩定的系統使用,由於它一直堅持着向前兼容,它不像新的庫,若是出了一個新版本基本上須要推倒重寫;而 Backbone 是寫比較複雜頁面的一個庫, Angular 、React 等等。編程

 

在這麼繁雜的前端中,單純寫前端業務有不少選擇。曾看到一個評論:「 2016 年,你完成一個巨簡單的業務,就須要 TypeScript 寫代碼,用 Fetch 發起異步請求,全部的代碼編譯程 ES6 ……」用了幾十個庫完成一個很是簡單的問題。那麼,在這樣的前端生態下,它必定會是繁榮的,若是不繁榮,不會有不少人在這裏作事情。後端

 

Web 技術和 JavaScript 到達各個領域

  • 後端:Node.js 在業務開發中已經比較普遍使用,並且 v8 性能較好。瀏覽器

  • 移動:最經常使用的 Hybrid ,React  Native ,NativeScript ,Weex 。前端框架

  • 桌面:Electron,nw.js 來實現 Web 端的應用,其實都是網頁。服務器

  • VR:WebVR ,A-Frame ,WebGL 

  • 硬件:Cylon.js ,Tessel ,Johnny-Five

  • 數據可視化:d3.js ,vis.js ,HighCharts ,Charts

 

由於 JavaScript 自己的代碼,學習陡峭程度很是低,入門門檻低,而且網頁端需求大,所以 JavaScript 異常繁榮。慢慢地,JavaScript 的性能愈來愈好,有更多人使用,進而寫 JavaScript 的人想用 JavaScript 寫更多的東西,一步步邁到了各個技術生態。

 

三足鼎立:Vue.js 、Angular.js 、React.js

2016 年,從繁雜的生態、無盡的爭吵和選擇當中, Web 開發中的 Vue.js 、Angular.js 、React.js 這三個框架初露端倪,各佔據一片江山。所說的三足鼎立有一個前提,並非它們在社區裏有多麼火或者人們都愛用,而是這些庫是否被當下最新的應用直接用在本身的業務代碼當中。

 

Angular.js 在 Google 已經被推了不少年,支持了 Google 自己及不少公司的大型業務代碼。React.js 是 Facebook 支持的項目,它已經被用在不少線上的業務代碼中,並且這些業務代碼天天在承接着幾億的訪問量。Vue.js 自己最開始是 Evan You 獨立開發者開源的項目,以後 Alibaba、餓了麼等公司都開始充分使用,如今阿里的 Weex 也借鑑了 Vue 的架構邏輯。

 

每一個框架甚至都有了本身的技術生態

三個庫三足鼎立的緣由是它們自己都有一套本身的生態。例如 React.js ,最先底下的 Server  Side  APIs 、GraphQL 、Flux 層怎麼樣把靜態數據狀態管理系統管好,再到 React 層自己頁面樣式,再到 Virtual  Dom 和 Native  Code ,它的技術量很少,若是深刻其中,學習週期也不長,可是它自己蔓延出了一條生態。若是有朝一日它把中間層作到足夠好,上下層對接不少東西,React 會成爲一個比較大的技術生態。

 

Why  Vue.js

咱們爲何選擇 Vue.js,這是一個很幸運、很偶然的選擇。掘金用 Vue.js 是在 0.12 版本,如今已是 2.15 版本。當時選擇最先版本的時候,掘金只有 4 我的。Vue.js 發展到如今,能夠看到是一個增加很是瘋狂的項目,從一開始的我的開源,到如今許多大公司使用,這和那些有大公司支持的開源庫有了很是大的區別。到如今,Vue 在 NPM 上每個月有超過 22 萬次下載,這是很高的量。

 

爲何用 Vue.js ?

第一次使用 Vue.js 的時候,公司想作促銷活動,寫一個問答宣傳頁面,當時微信尚未禁止這樣的傳播,我作了一個「算算你值多少錢」的應用,當時腦子裏有幾個庫。考慮到本身比較瞭解 Vue.js ,就試着用 Vue.js 來開發。後來發現從有這個想法到開發完只用了四個小時,包括 UI 層、頁面層、微博分享、微信分享,開發小東西的速度超乎了想象,但那時候尚未準備拿它來寫整個網站的業務邏輯。

 

Vue.js 到了 1.0 ,它是一個前端的 MVVM 的框架,看到一個網頁端的界面,它出現這樣的樣式必定是由於它背後有數據。而 MVVM 框架最大的特色是樣式隨着數據變化而變化,數據和 UI 層的同步是框架自己自動完成的,這是 Vue.js 在當時幫咱們解決的一個問題。Vue 到了 1.0 , MVVM 的框架適合掘金當時的業務開發需求。

 

圖 2

 

發展到 2.0,不少人說 Vue.js 已經很火了,不少人真正願意用它的緣由是這張圖(圖 2 ),它是一個漸進式前端解決方案。分了五層很重的東西,不是打包型的,而是一個把它拆散了,每一層根據需求會加的東西。最先期人們用 Vue.js 的需求,這是一段前端的業務邏輯,但願用聲明式語言 Declarative 把這段業務描述清楚,所以就能夠用 Vue.js 最簡單的業務邏輯、最簡單的庫把 Vue.js 這個庫加進來,即可以完成前端業務裏面的交互。從數據層到 UI 層的變化,特別簡單的一個功能。可是前端應用更復雜一點,這個頁面有不少組件,能夠根據本身的需求去定義 Component ,能夠用組建化的邏輯編寫業務邏輯,這是第二層。可是發現這個東西很複雜,一個頁面已經不能實現,要分好幾個頁面。能夠用另一個援引的庫,就像 Routing 加進來,有了前端路由。

 

如今發展這個業務愈來愈複雜,由於這個業務正好表明了公司本身的發展,剛開始掘金只是單純的 MVVM ,後來有了前端路由,再後來發現,這個頁面已經複雜到相似於小應用,小應用必定會帶來狀態管理。在這個網站上,全部的應用都要同步當下登陸的用戶,這時必須須要狀態管理,掘金便開始進行大規模狀態管理。

 

前端已經複雜到須要完整的一套技術體或者自動化工具,來生產 Build 測試、發佈等等,還要前端分包,這個頁面是純前端應用,不斷地打開新的頁面,其實它都是從後端再拿一個新的 js 出來,每一段頁面都是本身的 js ,這樣能提升性能,按需拿取頁面的邏輯,這個時候分包就必定要用工業化的邏輯來實現。再日後走,可能會有一些測試、單元、代碼的東西,它是一套整個的構建工具。

 

這就是一套流程,對於剛開始的開發者可能用特別簡單的 Vue.js 代碼寫一個特別帥的主頁,能動一動,彈一彈,後來能夠根據本身的需求修改,頁面能夠更復雜,能夠寫成組件化的、寫客戶端路由等等。這一套漸進式的系統,使得幾乎每個業務在用 Vue.js 的時候都有一個對標點,一個網站的對標點多是在客戶端流這一層,可能一個網站的對標點是在擴展工具。所以,一我的基於本身要作的業務,能夠按照不一樣的深度去使用,並且在不一樣的深度之下不會有性能或者學習路徑陡峭的問題,這就是人們喜歡用 Vue.js 的真實緣由。

 

Vue.js 原理

Vue.js 不支持 IE8 及其如下,它只支持 IE9 以上,由於 IE9 支持了 ES2015 。好比說 A 是一個 Object ,每次輸出 A 到 B 的時候,必定會先調用一次 getter ,至關於獲取了任何一個數據被改變的時候的那個事件,而且對於這個事件能夠進行相關的處理。

 

圖 3

 

這是一段業務(圖 3 ),這個業務可能基於相關的 Object 的數據,由於有 setter 函數在這裏控制,所以能夠生成一個 watcher 。面對每一段業務代碼,這個 watcher 都會關注全部相關的數據,以致於這些相關的數據發生任何的變更,它都會調動 setter 。setter 會告訴 watcher ,watcher 知道跟這段道路相關的數據發生變化了,發生變化以後就會去 Component  Render  Function,把新的數據的樣式給前端樣式,這樣完成了從數據層變化,到告訴 watcher ,watcher 再告訴 Render  Fenction,最後把前端 UI 變了這樣的邏輯。它並無用高級的數據結構或者高級的算法,它實際上是用了 JavaScript 原生的一個屬性。

 

選擇 Vue.js 框架

選擇一個前端框架必定有很重要的緣由:

  1. 開發效率:Declarative Rendering ,前端開發寫這個業務邏輯會很是漂亮;

  2. 代碼維護:組件化 vue-loader ,能夠在一個文件中關於某個組件或者某個頁面寫出邏輯層、樣式層,能夠寫在一個組建中,這是一個比較好的解決方案。

  3. 速度性能:要能知足需求,Vue.js 是遠快於 1.0 的。頁面渲染的時候可能不在乎性能,可是到頁面複雜度的時候便會很在乎性能,性能慢的時候會影響每個頁面跳轉。

 

掘金 Vue.js 架構

每次作一個新的頁面或者新的業務都會這樣操做,後端要作自動渲染、自動更新,會有一套配置文件來配置前端進行分包和不停加載,不停地把前端的業務融合在一塊兒。在每個頁面中最重要的必定是核心應用,在覈心應用中每次首要考慮的是路由,對於整個產品或者小的功能點是不是有一些共用的狀態。

 

定義好核心的應用清楚狀況下,在頁面裏面找基礎組件,而且把相關的基礎組件比較複雜地組合成一個公用模塊。基礎組件在上層調用組件的時候,上層能夠進行小的微調,可是這些組件的組合多是有公用模塊,模塊的意思是在上層使用這個組件的時候,不能夠再對這個組件進行任何的調整。再往下走是 Vuex ,也就是各個不一樣的分頁,這個分頁相關的業務邏輯,每次定義一個分頁,要把前端路由定義好,而且把分頁裏面須要的狀態拿好,把須要的組件和公用模塊拉進來,這個頁面的業務及直接單獨寫便可。

 

圖 4 

 

這是掘金一套前端的架構(圖 4),可是前端架構相比於後端架構,每每簡單不少。

 

純前端應用的弊端及問題

兼容問題

這三個庫( Vue.js ,  React.js ,  Angular.js: IE9+)都不支持 IE8 ,IE9 支持 80% 左右,偶然觸及到一些 Vue.js 很底層很極端的性能時,IE9 會掛掉,除此以外基礎性的還不錯。可是給企業端或者後端特別複雜的頁面,給工業用的 admin 頁面可能用的仍是 IE六、七、8 的瀏覽器,還不太能覆蓋這部分的需求。

 

SEO

純前端應用,若是看 Google 或者百度拉出來的數據,Google 作了一個後端的 cache ,跑了一個小的 Chrome 內核在後端,它能拉取徹底的純前端應用。而百度的機器一拉出來就是空的白頁面,什麼也沒有,並非百度的技術達不到。

 

第一,多是百度面對大多數的技術網站生態尚未不少的純前端應用。

 

第二,在後端小內核用純前端應用去抓挺費性能的,以爲沒有必要加這一層。可是面對中國的環境, Google 的流量很多,可是也有百度的流量,掘金要支持百度的 SEO ,可是還有其餘的 SEO ,國內的 SEO 其實都不太支持,搜狗支持,其餘都不太支持純前端應用的抓取,對於內容型網站來說多是一個坑。

 

速度

初始的拉取速度,若是是網頁的話,拉一個 HTML ,內容拿到了,開始往下看。掘金網站的真實狀況,速度還好,該出來的東西一秒以內都能出來,可是第一次拉一個 HTML ,再拉一個 js ,再拉數據,再渲染頁面,頁面出來再拉分別的數據。其實這套流程中,在 HTML 拉出一批小的數據出來。若是很追求性能極致的人是不太能接受的,並且永遠沒法解決。所以,若是很在乎初始頁面第一次 loading 速度的人,可能這裏會有問題。掘金如今已經有問題了,網站會在一個月內內容型頁面會變成徹底後端渲染。

 

URL <=> Content Cache

純前端應用可以作到的極致是每個資源都有一個 URL ,可是純前端應用很大的一個問題是:並非每個資源都有固定的 URL ,大多數的頁面都沒有一個固定的 URL ,這樣使得 cache 很難作。

 

每一個頁面都要定義分頁的相關邏輯,大多數的開發者若是沒有到達工業化或者產品沒有到達必定的數據量級,寫得很亂,並無作到每個頁面鬥毆本身的 URL 無,主流的 Cache  URL 模式很難執行。可是當產品不斷地優化,優化到必定的情景必定開始要提速的時候,純前端應用就會遇到極大的問題。

 

Vue.js 2.0 後端渲染

 

A Simple Vue.js Program

 

Virtual DOM

常常據說 Virtual DOM 很厲害,其實 Virtual DOM 就是把 HTML 用 JavaScript 來表現,它不是任何特殊的技術,沒有任何的功能點,能夠用 HTML 來表達一段 DOM ,也能夠拿 JavaScript 來表現一段 DOM 。最大的不一樣點在於,多了一層把 JavaScript 定義的 Virtual  DOM 渲染成真實 DOM 的這套業務邏輯。好比,這是一個 Virtual  DOM ,先把這個 Object 裏面再加一個 ul ,能夠用 Virtual  DOM 來實現,爲何說它的性能好呢?由於在瀏覽器環境中,HTML 或者 DOM 的直接運算很是慢,可是 JavaScript 運算很快。

 

Render Function 

圖 5 

 

有了 Virtual  DOM 這一層用 JavaScript 表明 DOM 以後,用 Render Function 把 DOM 再刷出去便可。所以,Render Function 也是 2.0 實現的,1.0 只能定義頁面和邏輯,它來幫你作一切,而 2.0 以後能夠用 Render Function ,這是一段把 Virtual  DOM 變成 DOM 的邏輯(圖 5 )。

 

最大的價值在於,由於有 Render Function ,把 JavaScript 變成真實 DOM 這個函數,一樣把後端能理解的 Object 在後端提早用 Render Function 輸出 HTML ,這樣後端就已經把它輸出來了,直接 Drive 給前端,這個頁面就已經有了。也能夠把一個 JavaScript 表達的 DOM 輸出成真實的 HTML 給前端,後端渲染就完成了。

 

Stream

只要在 Vue 業務包在一個 function call 中並接上 Window  contex,服務器 renderer 拿到相關業務 js 文件吐出內容。Vue.js 2.0 支持 Stream 後但流式數據,在 HTML 完整生成以前的向前端吐數據。

 

後端渲染 Nuxt.js 的開發實踐

Vue.js 最基礎的後端渲染,若是對於這樣一個業務,每一個公司都要根據本身的業務代碼作一套後端渲染的邏輯,這不太可能。對於通用解決方案,必定是有更好的庫,感謝有人造輪子。剛開始作後端渲染的時候是沒有輪子的,掘金後端渲染都是本身寫的,如今若是有輪子會好些。

 

開源支持

Vue 的生態繁榮,很大一部分來源於整個生態周邊環境的支持,好比腳手架、組件化、路由、狀態管理、 Ajax 、前端開發工具、前端組件庫、後端渲染。在 Vue 的前端方案上,中國已經比國外強,開發質量很高。後端渲染,早晚會有一個很牛的庫出來幫你們,很惋惜以前沒有,可是最後有了,叫作 Nuxt.js 。

 

Nuxt.js 是一個相似於 Next.js(React)的開源後端渲染庫,它支持的並非後端渲染這一層的業務,它作了一套通解,想要用 Vue 的業務去開發,但同時支持 code-splitting 、generation 等不一樣的配置文件,它都會有一套不錯的解決方案生成。可是你們都是後端的高手,最終可能不肯意用別人的解決方案。可是像比較偏前端的人來說,它的基礎解決方案已經解決很大問題了。

 

Nuxt.js 文件結構

它裏面有幾個基礎的文件定義,其中最重要的是 nuxt.config.js 。把分包打包的邏輯封裝到底層,這是如今最大的問題,由於有功能在這一層會作測試、靜態的傳輸和存儲,這也是爲何掘金不能直接去用 Nuxt 完成後端渲染,仍是要本身寫。最重要的是 Asssets 基礎業務代碼和第三方代碼的存儲文件,即 Vue 裏面不一樣頁面的這套邏輯。把一個頁面放在 pages 裏面以後,就不用專門定義,它會自動綁定好。

 

Nuxt.config.js

head 定義的是後端渲染這套業務的時候,在網頁端的 head 裏面放哪些基礎數據,好比 meta 等數據,以及 link 裏面有哪些靜態文件須要特別注意的,如何援引於其餘資源,好比 css 裏面掘金是從 assets 裏面拿出來的,它的分頁之間的切換,純前端應用不須要看到頁面裏面有一個 loading 的感受,它解決切換時候的動效,把它封裝得很漂亮。

 

pages

對於 Vue 來說,把它的 template 側寫在一個 export 的文件裏面,layout 、transition 和 scrollToTop 是純前端應用都會遇到的問題,這套頁面用的是哪一個 layout 展現?在頁面切換之間是否要有動畫效果?以及在純前端應用中每次頁面之間切換是否要滾到最上面?由於它是一個單純的頁面,若是不設置滾到最上面,會發現跳到另一個頁面仍是在中間的位置,可是在瀏覽器來看實際上是在一個網頁裏面,沒有跳到新的網頁,它把通用的需求封裝得很漂亮。validate 是解檢測 url 的,middleware 是一些其餘的功能,能夠再加進去。這裏面最好的事情是 head ,在純前端應用中會有不一樣的頁面,在每一個頁面中 title 必定會變,單獨頁面裏面移動端的展現模式和特殊的配置文件等等,這一套東西之前都得單獨來寫,每個頁面都得單獨解決,而如今通解來實現了,並且通解沒有作得太深,有時候開源庫定義得太死,可活動性太差,可是它定義好的東西都是全部人須要的。

 

Async  Data

拉數據,從遠端拉數據,再渲染頁面。

 

Vuex/Fetch

Fetch 和 data 幾乎是同樣,惟一的不一樣在於 data 這個函數是頁面渲染出來的,拉數據的時候在渲染頁面的更多樣式。打開一個頁面,Fetch 要先把頁面拉回來,這個頁面纔會跳轉。爲何要 Fetch ?由於對於後端渲染來說,必定是在後端渲染,必定是先把數據拉回來,才能把頁面生成,才能投給前端。因此,Fetch 函數是用後端渲染很重要的一個環節。

 

Vuex/nuxtServerlnit

Vuex 就是一個狀態管理器,也就是一個前端應用全部的數據都須要的地方。而這裏須要什麼呢?全部的後端頁面也須要用戶認證,而且把用戶數據給前端,可是對於純後端應用生成頁面稍微有點難,可是在 Vuex 裏面定義好全部頁面都須要公用這塊邏輯,而且用 nuxtServerInit 提早在後端也把這個需求、這個解取好,用這一套完整定義可使得前端、後端再輸出頁面,無論是前端輸出的仍是後端渲染好的,均可以同步得到這個數據,而且完成這部分業務。它解決了很是大的業務邏輯,若是讓本身寫,代碼量少說也得四五百行左右,它解決得很是好,掘金把源碼拿出來看明白,把這段源碼應用到產品裏。

 

總結

前端框架雖好,可是仍是須要後端渲染。Vue.js 後端渲染技術層已算成熟。Nuxt.js 等庫優化了後端渲染的實現效率。交互型產品適合前端應用,內容型產品適合後端應用。

 


 

歷史 ECUG 精粹系列:

謝孟軍:The State of Go

七牛雲大數據平臺建設實踐

Go 在遊戲行業中的工程實踐

相關文章
相關標籤/搜索