Web應用開發的七項原則

這篇文章主要介紹構建使用Javascript來控制UI的網站在設計時的7個原則。它們是我作為一名開發人員的經驗所得,也是我作為一名網際網路資深用戶的體會和總結。

Javascript毫無疑問早已成為了前端開發人員不可或缺的工具。但現在它的使用範圍還在不斷擴展到其他的領域,比如伺服器端甚至是微控制器。在斯坦福這樣的聲望卓越的大學裡面,它也已經被選為計算機科學入門課程的教學語言。

即便如此,它在web開發中究竟應該扮演什麼樣的角色或者說負責哪方面的作用,仍然是個迷:即便對於很多框架和類庫的作者而言也是如此:

  • JavaScript應該被用來替代像history,navigation和page rendering 這樣的瀏覽器函數么?

  • 伺服器端開發是不是到頭了?是不是根本就不該在伺服器端渲染HTML了?

  • Single Page Applications (SPAs) 是不是代表著未來的趨勢?

  • 一個網站和一個Web應用之間的區別精確的描述起來究竟是什麼? 是不是應該就是一個東西?

  • 在網站上,JS應該用來 增強 頁面的效果,而在Web應用中,則被用來 渲染 整個頁面?

  • 是否應該使用像PJAX或者TurboLinks這樣的技術?

下面就是我試著回答這些問題做的一些分析。我的分析是通過用戶體驗(UX)層面,特別是如何最小化用戶拿到他們感興趣的 數據 的時間,作為切入點,來驗證對Javascript的 各種 使用方式。我會從網路通信的基礎入手,一直說到對未來趨勢的預測。

  1. Server渲染頁面仍然是必須的

  2. 對用戶輸入立刻響應

  3. 數據變更時的應對

  4. 控制與伺服器的數據交互

  5. 不要破壞history,增強它

  6. 推送代碼更新

  7. 行為預測

 

1. Server渲染頁面仍然是必須的

TL;DR: 伺服器端渲染與SEO無關,它主要的考慮是性能:需要考慮的包括不在伺服器渲染的話,請求腳本、頁面樣式、頁面資源和API請求造成的額外的開銷,以及考慮在HTTP2.0里加入的PUSH of resources.

首先需要指出,在業界有一種錯誤的二分法:」server-rendered apps」 和 「single-page apps」的對立。如果我們的目標是用戶體驗和性能的最優化,那麼選擇其中任何一個而拋棄另一個都是錯誤的決定。原因其實很明顯:整個網際網路用於傳輸頁面的介質,有一個理論上可計算的速度局限。關於這點,Stuart Cheshire有個著名的文獻 (或者說是吐槽?),「It』s the latency, stupid」 :

The distance from Stanford to Boston is 4320km.
The speed of light in vacuum is 300 x 10^6 m/s.
The speed of light in fibre is roughly 66% of the speed of light in vacuum.
The speed of light in fibre is 300 x 10^6 m/s * 0.66 = 200 x 10^6 m/s.
The one-way delay to Boston is 4320 km / 200 x 10^6 m/s = 21.6ms.
The round-trip time to Boston and back is 43.2ms.
The current ping time from Stanford to Boston over today』s Internet is about 85ms (…)
So: the hardware of the Internet can currently achieve within a factor of two of the speed of light.

這裡提到的從波士頓到斯坦福路上花費的85ms,當然會隨著時間的推移不斷的改善:如果你現在測試一下說不定已經大大增速了。但需要注意很重要的一點:就算達到了光速,這兩個海岸間最少也需要 50ms 才能完成通信。

換句話說,用戶間連接的帶寬再怎麼顯著提高,花在傳輸路上的延遲總有無法突破的速度極限。所以,在頁面上顯示信息時減少請求次數,也就是減少信息被傳輸在路上的次數,對於良好的用戶體驗和出色的響應速度而言,至關重要。

這一點在Javascript驅動的Web應用流行起來之後顯得尤為明顯。這些應用一般<body>標籤內什麼東西都沒有,只有<script>和<link>標籤,被稱為」Single Page Applications」或者」SPA」。就像它的名字所暗示的一樣,伺服器返回時一直在重用同一個頁面,其他的頁面內容都是在客戶端被處理和渲染的。

考慮下面的這個場景:用戶在瀏覽器上訪問http://app.com/orders/,如果這是一個傳統的網頁,那麼在後台處理這個請求的時,就會帶回重要的 信息 ,用來完成頁面的顯示:比如,從資料庫裡面查詢出訂單,然後把它們的數據放在請求的返回裡面。但如果這是一個SPA,那麼第一次可能會立刻返回一個包含<script>標籤的空頁面,然後再跑一趟才能拿回用來渲染頁面的內容和數據。

圖1. 伺服器端發送的SPA的每個頁面組成結構分析

目前大多數的開發者都大方接受了這個額外的 網路傳輸過程 是因為他們確信這隻發生一次:後面反正是有cache的。也就是說,大家形成了這麼一個共識,既然整個代碼包一旦載入一次,就可以不用再請求其他的腳本和資源就完成對絕大多數的用戶交互(包括跳轉到應用的其他頁面)的處理,那麼這個開銷就是可以接受的。

但實際上,雖然有cache,腳本解析和執行的時間仍然會帶來性能上的下降。「Is jQuery Too Big For Mobile?」 這篇文章就探討了即便是載入一個jQuery庫,就會花去一些瀏覽器數百毫秒的時間。

更糟糕的是,和以前網速慢那種圖片慢慢載入的效果不同,如果是腳本正在載入,用戶什麼都看不到:在整個頁面被渲染出來之前,只能顯示空白的頁面。

最重要的是,目前網際網路數據傳輸主要的協議TCP 建立 比較慢。

首先,我們知道,一個TCP連接先需要握手。如果處於安全考慮使用了SSL,就還需要額外的兩個來回(客戶端重用了session的話,也需要一個額外的來回)。這些流程完畢之後,伺服器才能開始往客戶端發送數據。換句話說,再小的代碼包實際上也需要幾個來回才能完成傳輸,這就讓前面描述的問題變得更加糟糕。

其次,TCP協議裡面有一個流控機制,被稱為 slow start,也就是在連接建立過程中逐漸增加傳輸的分段(segments)大小,入下圖所示:

圖2. 伺服器端在TCP連接的不同階段能夠發送的分段大小(KB)

這對SPA有兩個很大的影響:

  1. 文件比較大的腳本,花在下載上的時間比你想象中的要長得多。Google的Ilya Grigorik在他的專著「High Performance Browser Networking」 裡面說過,「4個來回(…)和數百毫秒的延遲都花在從伺服器下載64KB的文件到客戶端上了」,從前面的圖也可以看到,基本是比較高速的網路連接,比如倫敦和紐約之間,一個TCP連接要達到最大速度,也需要花上大概225ms。

  2. 因為前面說的延遲對首個頁面訪問也是有效的,所以你讓什麼數據最先被傳輸就顯得非常重要了。Paul Irish在他的演講「Delivering the Goods」給出的結論是,一個Web應用最開始的 14kb 數據是最重要的。

在足夠短的時間窗內完成內容傳輸(哪怕只是呈現基本的沒有數據的layout)的網站,就是響應良好的。這也是為什麼對於很多習慣了在伺服器端處理數據的軟體開發者覺得Javascript很多時候根本沒必要用,或者是在很有限的情況下用用就行了。當這些開發者使用的是配置良好的伺服器和資料庫,又有CDN來做部署和分發時,他們這種感覺會非常明顯。

但是,伺服器在輔助和加速頁面內容的分發和渲染中應該被怎麼使用,也是需要根據每個應用場景仔細分析的,絕對不是「把整個頁面交給伺服器渲染吧」那麼簡單的事情。在一些情況下,如果頁面上的內容對用戶並不是非看不可的,就可以不放在第一個響應中返回,而是讓客戶端在後面的操作中到伺服器去取。

比如,有的應用會先把一個」殼」頁面返回給客戶端,然後在這個頁面上併發的請求多個部分的數據。這樣即使在後台連接速度較慢的情況下,仍然能夠有較好的響應速度。還有的應用會把 「瀏覽器裡面的第一個整屏」 顯示的頁面做預渲染。

伺服器能夠根據當前處理的session,用戶和URL對腳本和樣式文件進行分類也是很重要的。舉例來說,用來對訂單進行分類的腳本,對於/orders這個URL顯然是重要的,而處理」首選項」的邏輯的腳本就不那麼重要。再比如說,我們可以對CSS樣式表進行分類,比如區分「結構性的樣式」和「皮膚和模板的樣式」等。前面這類很可能對Javascript的正確運行是必須的,因此需要 阻塞 的方式載入, 後面這類則可以用非同步的方式載入。

到目前為止,在伺服器端處理一部分或者所有的頁面,仍然是避免過多客戶端與伺服器的交互的主要手段。StackOverflow in 4096 bytes很不錯地展示了如何降低和伺服器的來回交互次數。作為概念驗證的SPA,它理論上可以做到在握手后的第一個TCP連接中完成載入!當然,要做到這些,它使用了SPDY 或者 HTTP/2 server push,因此可以在一個hop裡面傳輸所有客戶端可以緩存的代碼。

圖3. 使用了內鏈CSS和JS技術的Stackoverflow in 4096 bytes

如果我們有一個足夠靈活的系統,可以在瀏覽器和伺服器直接共享渲染頁面的代碼(比如雙方都是js),並且提供工具增量的載入腳本和樣式,那麼 網站 和 Web應用 就可以合一而不再是兩個模稜兩可難以區分的詞了:它們本身就有一樣的UX要素。比如一個博客頁面和一個複雜的CRM,都有URL,都需要跳轉,都展示數據,本質上並沒有太大不同。即便是像數據表格這樣複雜的東西,傳統上主要是客戶端提供的功能來完成對數據的處理,但也首先需要給用戶展示那些需要他處理的數據 。降低客戶端和伺服器交互的次數,對實現我們說的這樣的系統非常重要。

在我看來,我們看到的大量系統上採用了這樣那樣性能上的權宜之策,是因為整個技術棧的複雜度在不斷累加。Javascript和CSS這樣的技術是被逐漸加入到系統的,它們的風靡又花了一段時間。儘管有人希望在協議上做出改進,來增強性能(比如SPDY或者QUIC),但應用層顯然才是最需要改進的地方。

要理解速度的重要性,去重溫一下WWW和HTML創立之初的一些討論是非常有用的。特別是在1997年提議在HTML里加入img這個標籤的時候,Marc Andreessen在下面這個郵件thread里反覆強調了提供信息的速度有多麼重要:

「If a document has to be pieced together on the fly, it could get arbitrarily complex, and even if that were limited, we』d certainly start experiencing major hits on performance for documents structured in this way. This essentially throws the **single-hop principle of WWW** out the door (well, IMG does that too, but for a very specific reason and in a very limited sense) — are we sure we want to do that?」

 

2. 對用戶輸入立刻響應

TL;DR: 我們可以使用JavaScript來掩蓋網路的延遲,把它作為設計原則,就可以在你自己的應用裡面去掉絕大多數的spinner或者loading。使用PJAX和TurboLink的話,你就會失去了這些改善用戶速度體驗的機會。.

第一個原則里,在描述為什麼要盡量減少前端和後端之間數據來回傳輸的次數時,主要是基於傳輸速度有理論上限的事實。實際上另一個需要考慮的要素就是網路的質量。我們都知道,當網路連接狀況不好時,就會有數據包需要被重傳。所以,你覺得應該一個來回就傳輸完畢的數據,可能實際上要花去好幾個。

在這方面,Javascript正好可以幫上忙:通過客戶端的代碼來驅動UI,人工的構造出零延遲,就可以掩蓋網路的延遲,製造一切操作都很順暢的假象。比如,網頁和網頁之間是通過超鏈接,<a>標籤,鏈接在一起的。傳統網頁上,當一個鏈接被點擊時,瀏覽器就發送一個可能會耗時很久的請求,然後處理請求並把內容呈現給用戶。

但Javascript允許你立刻響應(有些地方把這個叫樂觀響應):當一個鏈接或者按鈕被點擊時,頁面立刻做出響應而不需要去訪問網路。這方面著名的例子就是Gmail(包括最近Google的新產品Inbox)的」郵件歸檔」功能。當你點擊」歸檔」,UI上郵件立刻會被顯示為歸檔狀態,而伺服器的請求和處理是非同步進行的。

再比如,我們處理的是一個表單。也許你覺得一個表單在數據被提交到伺服器,處理結果返回之前,不能做太多的事情。但其實當用戶完成輸入並點擊提交的時候,我們就可以開始響應了。甚至有些做到極致的應用,比如Google搜索頁面,當用戶開始輸入的時候,展示搜索結果的頁面就已經開始渲染了。

圖4. Google在用戶輸入搜素關鍵字時就開始渲染搜索結果頁面

這種行為被稱為 layout adaptation。 它的思路是當前頁面知道操作后狀態的頁面layout,所以在沒有數據填充的情況下,它就可以過渡到下面那個狀態的layout。這樣的處理是」樂觀」的,是因為有可能後面那個頁面的數據一直沒有返回,而這時候頁面的layout已經畫在那裡了。

Google的主頁的演進,非常清楚的說明了我們這裡強調的第一和第二個原則。

首先,分析訪問www.google.com時TCP連接的包數據可以看到整個首頁的數據都被一次性發出來了。整個交互,包括關閉連接,耗時幾十毫秒而已。而且,似乎在Google一開始的版本就做到了這點。

在2004年晚些時候, Google標桿性地使用了JavaScript完成輸入時動態提示功能(和Gmail一樣,也是一個20%創新時間產出的項目),這一功能也啟發了很多網站開始大量的使用AJAX:

Take a look at Google Suggest. Watch the way the suggested terms update as you type, almost instantly with no waiting for pages to reload. Google Suggest and Google Maps are two examples of a new approach to web applications that we at Adaptive Path have been calling Ajax

到了2010年,Google又推出了及時搜索,也就是我們前面看到的效果:當用戶輸入關鍵字時,整個頁面無需刷新就可以展示搜索的結果。

另一個例子是iOS。在很早期的版本,iPhone就要求開發者提供一個default.png圖片,用來在應用被載入完成之前顯示給用戶:

圖5. iPhone OS強制在應用載入前顯示一個default.png

當然,這裡OS不是在隱藏網路延遲,而是CPU處理延遲。對於iPhone初期版本來說,這樣來彌補硬體的弱點非常重要。當然就和網頁上使用提前加載一樣,這種手法有可能會崩壞:當載入來的數據和default.png不匹配的時候。Marco Arment在2010年對它可能帶來的影響進行了 透徹的分析。

除開處理表單和輸入,Javascript還被大量用於處理文件上傳。我們可以通過各種前端表現來滿足用戶上傳文件的需求:拖拽,粘貼以及各種file picker。特別是有了HTML5的新API之後,我們可以在文件完成傳輸前就顯示它的信息。在Cloudup網站的上傳文件中,就使用了類似的實現。從圖片中可以看到,在用戶選擇了文件之後,縮略圖就立刻生成並顯示在用戶界面上了:

圖6. 在上傳完成前圖片就被顯示出來並且加入了虛化效果

上面的方式都是採用前端技術來製造速度的假象,但這種方式其實在很多地方都被證明是有效的。一個例子是在美國休斯頓

你可能還喜歡如下這些文章

20056700簽名
葫蘆娃的更多精彩文章請進入頻道: 20056700
  1. 樂高發布支持iOS控制的 MINDSTORMS EV3 機器人玩具
  2. 科學家用骨骼肌研製「生物機器人」 
  3. CubeSensor—你的室內健康顧問
  4. Facebook將神奇動畫引擎Pop開源了
  5. Web應用開發的七項原則
  6. 沙發、睡床與獨立小屋
  7. 十種埋沒你潛能的生活方式
  8. 五分鐘內打動人心的交際法則
  9. 研發氫燃料發電驅動機 環保無污染汽車或將問世
  10. 對php安全有幫助的一些函數
  11. 2014年度10大流行語 ,你也就聽過一兩個吧
  12. 一個女人必須知道的最實用的139條生理常識
  13. 充電寶手機殼
  14. 優雅女性穿衣8大經典原則,女人必看
  15. 一夜暴富的遊戲開發者 4億多元購豪宅開派對
  16. 美國90後用戶是怎樣評價這些社交網站的
  1. 空中巴士,中國原創,又一次驚艷了全世界!
  2. 網站運營的基本思路-六部曲
  3. iPhone7已曝光,太炫了! 亮瞎眼!
  4. 醫學家稱人死後有「來生」 進入多元宇宙
  5. 人類無法再活千年 需尋找出路