在對英才網企業線前端不斷的完善過程當中,咱們嘗試進行了先後端分離,引入Node環境、以及在使用React的過程當中,自行開發DOM渲染框架,解決React兼容低版本IE的問題,在這個過程當中,咱們有了一些經驗和體會,但願本文對您有所幫助。前端
原有架構下,後端系統基於Java語言實現,經過Velocity模板實現服務器端渲染,前端同窗維護靜態資源,在維護頁面狀態時,還須要模板和腳本互傳參數,模板中還會有不少UI邏輯,等等,先後端耦合很深,這種模式下開發效率很是低,聯調成本很是高,同時Velocity的模板和前端代碼若是是分開管理和維護,針對前端靜態資源的的構建流程不方便對Velocity模板進行靜態資源的引用替換,影響了自動化發佈流程,也限制了前端的發展空間。git
先後端分離包括先後端開發分離,以及先後端物理分離,先後端的開發分離仍是基於原有的架構,經過提供相關的平臺工具,好比Jello,Mock等,彌合先後端耦合的點,使先後端開發工做能夠獨立開展,後期經過平臺無縫集成。github
先後端的物理分離是徹底的系統級別的分離,好比Web服務器採用PHP或者Node,或者前端同窗去學Java語言,徹底接管了Web服務器這一層的開發,不過對前端同窗的要求過高了,不是每一個人都是全棧工程師,相比PHP和Java,Node對前端同窗來講,學習成本低了一些,不過僅僅是語言層面,從瀏覽器運行時環境切換到服務器運行環境仍是須要更多的實踐經驗,好比我遇到不少前端開發同窗想學習Node,可是無從下手,若是不切換到後端思惟,仍是無法深刻Node的。算法
開發分離相比較物理分離更難一些,由於對平臺工具要求比較高,引入了新的工具,也引入了新的維護工做、流程和規範,若是前端同窗沒有按規範編寫模板,雖然在開發環境下完美運行,可是後面發佈到運行環境,仍是有必定的風險的,畢竟在開發模式下,模板的運行環境是不一致的,對前端新手來講,學習規範也須要很多成本。數據庫
物理分離相對更容易一些,前端接管Web服務器,開發和運行環境是一致的,不過對前端同窗的要求更高了,還須要瞭解Web服務器相關的知識,Node對前端來講,入門成本仍是比較低的,引入Node之後,針對Node的監控也是一個問題,Node是單進程單線程,若是不能很好的處理異常,進程會掛掉,雖然能夠用pm2等進程管理器能夠保證進程穩定性,可是異常就是不可預測的錯誤。後端
還有關於純瀏覽器端渲染,也就是SPA,也是一種分離,純瀏覽器端渲染的狀況下,先後端耦合進一步下降,只是數據接口的耦合,經過Mock服務約定一直的API,先後端並行開發。瀏覽器
關於先後端分離的更多內容能夠看看玉伯的這篇文章[:Web 研發模式演變](https://github.com/lifesinger/lifesinger.github.com/issues/184)前端框架
原有的項目目前仍是基於後端的MVC架構,後續隨着改版逐步遷移到瀏覽器端渲染,Node環境準備好之後,提供基於Node的服務器端渲染能力。服務器
上文提到,企業線是不須要作SEO的,能夠作純瀏覽器端的渲染,不過爲了更好的優化加載體驗,以及將來在其餘業務線的改版作技術儲備,能夠在企業線嘗試去作。架構
要實現的目標就是,一套組件代碼,同時支持前端渲染和後端渲染,這樣就不須要爲SEO去作些折中的方案了。
整個迭代的過程大體分三個階段:
企業線的前端框架選型經歷了一段時間的考慮,讓人糾結的點在於,不少新興的框架對低版本IE支持很差,好比AngularJs、VueJs、ReactJs,可是咱們又不能放棄低版本IE,我我的比較傾向React,下面我列出幾點:
若是咱們選擇兼容IE的框架,目前只有Avalon,Backbone等,這樣的框架要麼過期,要麼生態欠佳,將來UI的擴展會受限制。
最終,咱們仍是選擇了React生態,並且,自行實現了針對低版本IE的DOM渲染,達到了兼容低版本IE的目的。
爲了實現兼容低版本IE,咱們針對React進一步深刻了解,好比關於Jsx的編譯,虛擬DOM的渲染,diff算法,flux架構等
React引入了Jsx語法,實如今JavaScript中內聯的方式書寫HTML,相比以前的字符串模板引擎,開發體驗上了一個臺階,咱們很是但願能使用Jsx開發咱們的組件,Jsx最終是如何被渲染成真是的DOM的呢,在github上,有很多針對jsx的轉換器,好比:
l nativejsx:JSX to native DOM API transpilation.
l jsx-transform :This module aims to be a standard and configurable implementation of JSX decoupled from React for use with Mercury or other modules.
l jsx-runtime:Runtime for rendering JSX-IR. Runtime does not renders JSX-IR, otherwise it provides common interfaces for Renderer to be implemented.
上面三個項目都實現了對Jsx的解析,好比nativejsx,將Jsx編譯成真實的DOM建立代碼,jsx-transform將Jsx獨立出來,可使用在 其餘的vdom技術,jsx-runtime提供了更靈活基礎庫,藉助這個庫,能夠方便的將jsx轉換其餘的想要的格式,具體如何轉換,能夠本身控制,好比Jsx轉換成DOM建立代碼,轉換成React須要的的格式,或者轉換成字符串等。
具體到React中,轉換過程是這樣的:
爲了兼容低版本IE,咱們嘗試本身作了DOM的渲染,和React比較,去除了虛擬DOM,過程以下:
渲染過程兼容了React的API,好比:React.createClass,React.createElement,這樣之後能夠平滑切換到React,而且能夠和React進行性能的對比。
關於去除虛擬DOM,有利也有弊。
有利的是首次渲染的性能獲得提高,React原有的方式是先構建虛擬DOM樹,而後用虛擬DOM樹去構建真實的DOM樹,這個過程確定是有性能損耗。
弊端是增量更新沒法作diff,React的高性能體如今經過diff算法實現對DOM的增量更新,若是系統增量更新很少,虛擬DOM就沒必要要了。
其實最佳的方式是首次渲染就是服務器端渲染,增量更新採用React的模式,若是能夠控制頁面那些組件服務器端渲染,哪些作前端渲染,就更好了。
這裏作個數據對比,
庫文件大小:
渲染時間對比:
去除虛擬DOM後,雖然帶來性能的提高,可是沒有虛擬DOM的React,不能稱之爲React,React更多給咱們帶來的是對UI架構的從新思考,加入一個抽象中間層,多端可以共用一套狀態數據,和狀態變遷邏輯,而UI的渲染能夠針對多端去實現,這樣爲將來帶來了很好的擴展能力。
在狀態管理方面,咱們目前沒有引入流行Redux或者alt等Flux框架,官網提供的Flux的實現又很複雜,Redux框架又不是很瞭解,因此暫時先實現了一個輕量級的Flux框架,不過,實現很是簡單,每一個組件一個Store,UI組件監聽Store的change事件,Action負責Store數據的維護。
後端API的調用放在Action中,Store存在的做用在於方便多個View共享數據,好比一個View能夠監聽多個Store的change事件。
這個Flux實現和官方的比較,差異很大,官方實現以下:
主要的差異以下:
相比之下,咱們的的設計只能是剛剛夠用,缺點是Action和View的關係,Action和Store的關係,都是強耦合的,相比較以後才瞭解了官方設計的精髓。
還有另外一個問題,頁面是用多個Store,仍是使用使用一個Store,這個經典的Flux實現和Redux實現的差異,當咱們想實現服務器端的渲染時發現,單一的Store是最好的,而多個Store,數據準備很是不方便。因此將來,咱們會引入Redux。
Node環境最近才準備好,Node的優勢是很簡單,缺點就是太簡單了,以致於在複雜的業務線上,不敢投入使用,好比遇到異常容易崩潰的單線程,好比多層的回調代碼,過於自由JavaScript語言帶來的不可預測性,相似Java、.Net這樣的語言,有些錯誤能夠在編譯期暴露,而JavaScript執行期才知道是否有Bug,過於自由,代碼混亂,可讀性差,以致於有時候改別人的代碼不如本身再實現一套。
爲了解決JavaScript自己的問題,出現一些及技術,加強JavaScript語言的健壯性,好比Facebook的Flow,微軟的TypeScript,還有CoffeeScript,均可以彌補JavaScript的類型過弱的問題。
隨着對ES6的支持愈來愈好,對異步和麪向對象方面方面JavaScript的支持也愈來愈好,使JavaScript具有構建大型應用的能力。
關於Node的進程監控,初期在運營線搭建了套監控系統,用到以下的系統:
l StatsD:基於Node,用於向graphite的收集器發送數據
l Carbon : 後臺服務,監聽端口數據(TCP/UDP)
l Whisper :數據庫,存儲時間序列的數據
l Graphite:基於時間序列數據庫的圖形展現系統
l Grafana:強大的圖形自定義功能,從Graphite抽取數據
基於這個系統的監控截圖:
不過系統按最低的標準配置,沒有集羣,只是在單一業務裏嘗試用,大面積用恐怕支撐不了,最近,咱們將Node的監控接入集團運營部的open-falcon,架構部的WMonitor,Node的監控問題也終於解決。
引入Node對先後端分離很是有利,前端靜態資源的構建、發佈單獨走流程,不須要對其餘系統的依賴,還有Node對服務器端渲染的支持,愈來愈多的公司在作同構直出,藉助Node解決SEO的問題。
在支持服務器端渲染方面,React有很好的支持,能夠將React組件渲染成HTML,或者藉助前文提到的Jsx轉換工具,直接能夠將Jsx轉換成HTML,方法仍是不少的,前端渲染和後端渲染的過程大體以下:
須要注意的兩個地方:
1.Store數據準備:
前端渲染和服務器端渲染不一樣的地方是,前端將渲染分階段進行,好比,通常會分三步:
1) UI框架展示,不加載數據
2) 發起加載數據的異步請求,UI處於Loading狀態
3) 數據加載結束,從新渲染UI
後端渲染須要先後端配合:
1) 後端數據準備,渲染出HTML
2) 前端作事件綁定,完成後續操做
2.導航
前端導航經過URL的hash實現導航,頁面不刷新,後端導航,須要請求回發到服務器端,從新實現一次渲染。導航組件包括:站點導航、分頁導航,Tab導航等。
前面提到Redux的狀態管理,整個頁面組件用一個Store,很是利於頁面的服務器端渲染,只須要將Store數據準備好,整個組件就能夠實現渲染,如何渲染,徹底取決於Store的數據,好比,對須要服務器端渲染的組件,提供數據,不須要渲染的,不提供,後續在瀏覽器端判斷,作異步的加載,若是能實現這樣一套的組件架構,會提高開發效率,較少工做量,會有更好的加載體驗。
本文討論了先後端分離的一些想法,特別是針對Node實現大前端,自定義Jsx的渲染,基於React+Redux支持先後端渲染,有些想法尚未落地,後續會逐步探索,但願本文對你有所幫助。