更新: 本文本來的標題是「爲什麼咱們棄用AngularJS:……」,如今把它去掉了。由於這些痛點主要是針對單頁JS應用框架的。有些人認爲本文是專門批判AngularJS的,這可不是個人本意。-- Quinnhtml
幾個月前咱們的Sourcegraph網站向公衆開放,它是一個富AngularJS應用。服務器傳輸原始的HTML頁面和JSON端點,剩下的就交給Angular來處理。這是一個建立Sourcegraph的簡易方式,當時咱們不知道Sourcegraph會變成什麼樣。前端
可是單頁JavaScript框架並不適用於每個站點。Sourcegraph是一個內容爲主的站點,咱們漸漸發現富js應用弊大於利。富js應用的好處衆所周知,下面是咱們體會到的一些意料以外的困難。但願對面臨相似選擇的開發人員能有一些幫助。git
咱們早知道會面臨不少的困難,可是不知道會有這麼難。angularjs
搜索引擎爬蟲和社交網站的預覽抓取器不能加載純Javascript站點,提供替代版本又慢又複雜。github
有兩種方式能夠容許爬蟲閱讀你的站點。你能夠在服務器端運行一個瀏覽器實例來執行你的應用裏的Javascript,而後從DOM中卸下HTML(使用PlantomJS或者WebLoop)。或者你能夠建立一個服務端生成的專供爬蟲的替代性HTML版本。web
第一個方法須要你爲每個頁面加載創建一個headless瀏覽器(或者tab),比起直接產出HTML,這樣會花費不少的時間和系統資源。取決於你使用的框架,須要很多精力來決定何時頁面已經準備好了。 你能夠緩存頁面,可是若是頁面常常改變,那麼緩存只能起到很是有限的優化做用,並且會增大複雜度。這個方法會將你的頁面加載速度拖慢好幾秒,對搜索引擎排名也不利。gulp
第二個方法(建立一個替代性的服務器端站點)對簡單站點而言足夠了,可是若是頁面不少,這將是一個噩夢。何況若是Google認爲你的服務器版本站點跟你的主站版本有很大的不一樣,那他就會狠狠的懲罰你。糟糕的是,直到你的訪問量直線降低的時候你纔會意識到你已通過界了。segmentfault
不少分析工具須要使用易於出錯、手工集成的HTML5 history API(pushState)來導航。這是由於它們沒法自動檢測到你的應用使用pushState導航到了新的頁面。即便能夠作到,它們仍然須要等待你應用的信號來收集新頁面的其餘信息(例如頁面標題和其餘頁面特定的指標)。瀏覽器
你如何解決這個問題?同時取決於你的客戶端路由庫和你集成的分析工具。用Google分析和Backbone.js?嘗試一下backbone.analytics。用Heap(順便說一下,Heap很棒)和UI-Router?設置你本身的$stateChangeSuccess
鉤子而後調用heap.track緩存
還沒完!你想追蹤起始頁面加載?也許你重複跟蹤了?你會跟蹤失敗的頁面加載嗎?若是你使用replaceState代替pushState呢?即便要獲知你是否錯誤地配置了分析鉤子——或者是否依賴升級搞亂了系統——也是至關困難的,除非交叉檢查分析。當你發現問題後,很難去恢復你錯過的分析數據(或者消除重複數據)。
前端JavaScript構建工具,例如Grunt,須要複雜的配置並且會很慢。還好咱們有像ng-boilerplate這樣出色的項目來幫忙,可是它們很慢。而且若是你想添加一個自定義的步驟的話你仍是沒法避免複雜性。(我爲何說Grunt複雜,看看這個配置文件就知道了。)
一旦你配置好了你的應用,包括Gruntfiles等等。你仍然要忍受漫長的JavaScript構建時間。你能夠把dev和production構建通道分開來提升開發速度,可是你終將深受其苦。用AngularJS尤爲如此,他須要在壓縮代碼前使用ngmin(若是你用了特定功能)。事實上,咱們有幾回就是由於這些壓縮的JavaScript和開發時的代碼表現不一樣而把SourceGraph搞砸了。
事情正在改善,Gulp是一個巨大的提高。
測試JavaScript-only的站點須要使用基於瀏覽器的測試框架,好比Selenium,PhantomJS,或者WebLoop。安裝這些(除了PhantomJS)一般意味着安裝WebKit和Java依賴,配置Xvfb(雖然新版的PhantomJS移除了這些依賴),也許運行一個本地的VNC客戶端和服務器來測試。最後,你還須要在持續集成服務器上配置這些東西。
相反,測試服務器端生成的頁面一般只須要類庫來獲取URL和解析HTML,安裝和配置要簡單許多。
一旦你開始編寫瀏覽器測試,你必須處理異步加載。你不能在頁面尚未加載的時候就測試頁面上的元素,可是若是在一個特定時間段裏沒有加載,你的測試就會失敗。瀏覽器測試類庫提供了一些幫助函數來處理這種狀況,可是對於複雜頁面它們只能幫上一點小忙。
你想組合很重的瀏覽器測試工具(Selenium,加上Firefox或者Webkit)和很大的測試複雜度(因爲瀏覽器測試的異步本性)?你的測試須要不少配置,很長的時間來運行,並且很不可靠。
在富JavaScript應用中,頁面轉換幾乎是瞬間發生,而後全部的特定元素異步加載。服務器端應用偏偏相反:頁面在服務器端加載完成前不會發送到客戶端。
聽起來彷佛是客戶端應用勝利了,可是也許這不過是一個假裝的詛咒。
考慮客戶端JS應用,當用戶點擊一個連接,頁面會馬上加載並呈現。若是用戶導航到一個側邊欄須要5秒鐘才能夠加載的頁面,第一眼感受很快,可是若是用戶須要的信息在側邊欄裏,對用戶來講就太慢了。即便你須要的特定內容能當即加載,你仍須要忍受轉動的加載指示器和頁面填充時的抖動。
如今考慮一下這樣的狀況:若是開發人員想在那個頁面添加新功能。很難肯定這個功能是否必須快速加載——由於一切都是異步的,因此誰會在乎頁面底部過了幾秒才加載呢?如此反覆幾回,整個站點就會讓人覺察到遲緩和抖動。
在服務器端應用中,若是一個API調用很慢,整個頁面就會阻塞直到頁面完成。服務器端的緩慢不可能被忽視,由於這很容易被測量,而且會公平地影響每個人。可是在客戶端應用中這很容易被忽略。
你能夠爭論說,一個好的開發團隊應該避免這些錯誤,而且客戶端 JS 框架不是罪魁禍首。這是對的,可是整體上來講,客戶端JS框架下降了遲緩的開銷。這一點觸動了開發團隊的激勵機制。
上面說的問題,自己都不算大問題。咱們能夠作不少工做來減輕上述狀況(事實上咱們確實作了不少)。可是,這些問題加在一塊兒就是另外一回事了,能夠說,客戶端JS框架成爲了咱們開發工做的一大負擔。
同時要牢記,每個站點都是不一樣的。例如,Sourcegraph是一個內容站點,這意味着頁面在加載後不會有太多的變化(和富應用相比)。咱們依然喜好這些技術,可是它們不是構建咱們的主站的合適工具。
原文 5 surprisingly painful things about client-side JS
翻譯 SegmentFault