Web App防坑手冊

最近幾年,隨著前後端分離、單頁面應用的崛起,網頁正變得越來越應用化。移動互聯網端的發展更是助長了這個趨勢——對於交互、性能不敏感的場景,Web App在開發成本、跨平台兼容上有著明顯優勢。

但在這火爆的行情背後,很多時候從產品經理到設計甚至開發,對Web平台的特性並沒有足夠的了解與警覺,導致最終產品成了既不App也不Web的四不像,不僅拖累用戶體驗,開發團隊也容易無所適用。

這篇文章希望圍繞著Web的特性,探討Web App與Native App的不同,幫助讀者在項目中儘早地識別出可能出現問題的場景。

Web App is NOT App

Web App 也要按照基本法

Web App或者單頁面應用(Single Page Application),名字裡帶個App,聽起來頗有鳥槍換炮的味道,無論是工程師還是利益關係人(產品經理、客戶),難免會有」和尚摸得我摸不得,App做得我做不得?「的錯覺。更有甚者,把原生App的設計稿P上個瀏覽器地址欄就丟給團隊,讓照著做個Web App出來。如果這是你遇到的情況,那麼恭喜,你已經在被坑的路上了

俗話說入鄉隨俗,既然運行在瀏覽器上,即使Web App能很大程度上提供接近App的體驗,但有幾點,是從根本上不同於App而又必須考慮的:

1. 應用入口與訪問路徑

App對用戶的訪問路徑擁有近乎絕對的控制權:用戶從哪些地方進入應用(圖標、通知欄、分享鏈接)有限且可預測。而每一頁提供的操作入口之和,都是用戶當前可訪問的子路徑的全集。

而Web對用戶的訪問路徑控制權近乎為0:任何頁面的URL都是對用戶可見的,用戶可以添加書籤,分享給親友——任何頁面都可能被直接訪問,必須在設計和架構層面考慮。

鑒權和被訪問並不矛盾:本文中被訪問嚴格來講是被用戶請求,而鑒權或重定向則是對請求的處理

2. 刷新、後退、前進

這三個操作是所有Web瀏覽器工具欄的標準配置,而App多數情況下只需要考慮返回操作。

最麻煩是刷新:它會清空頁面內存、重新發起請求、重新執行腳本。這意味著Web端跨頁面的內存數據是不可靠的,如果A頁面依賴了B頁面放在內存中的數據,下次刷新時得到的只會是undefined。

3. 上下文隔離

用戶可能在應用或站點上進行一系列的、跨頁面的操作,這些用戶行為稱為上下文,而操作產生的數據(比如用戶選擇或表單輸入),本文接下來稱作上下文數據。

由於App對路徑擁有絕對的控制權且不考慮刷新,某種程度上說App打開新的視圖和在網頁上打開模態窗的成本是一樣的,下游頁面可以假定上游頁面生成的內存數據一定存在,而這些數據完全通過代碼存取,對數據結構沒有限制,因此App端的上下文數據是複雜、可依賴的

反觀Web,由於每個頁面都可以被直接訪問、刷新、甚至是複製鏈接到另一台設備上訪問,存儲在內存、LocalStorage中的數據都因此變得不可依賴,唯一能可靠傳遞的上下文信息僅限於URL可以表徵的數據,在沒有伺服器幫助的情況下,Web端的上下文數據是簡單或不可依賴的

現代移動端瀏覽器提供了一種應用模式,可以屏蔽瀏覽器工具欄,讓頁面更像原生App。然而現階段真實應用的案例並不多見,並且至少有兩個問題:

  1. 後退無法完全屏蔽:安卓平台的系統返回鍵等同於瀏覽器的後退。

  2. 移動端隨時吃緊的內存可能導致當前不活動的頁面被內存回收,下次打開時重新載入——相當於一次刷新。

為什麼URL重要?

上面三點其實都圍繞著一個核心——URL,某種程度上說,整個Web世界都是被URL標識並驅動的,每個URL都應該定位到相應的資源

而現實的情況是:可能你在做一個面向消費者(以下稱2C)的項目,但需求方壓根就沒提URL和刷新的事;也可能你做的只是一個後台管理系統,不會有人在乎這東西,甚至會有一些和URL的定位特性相悖的需求。

這是最可怕的事,也是我寫這篇文章的初衷:在真正遇到麻煩前,很少有人(甚至包括資深的前端工程師)會全面的思考這類問題,等掉到坑裡的時候才發現積重難返。

在不假思索地接受這些設定前,希望你能仔細思考以下幾個問題:

  • 客戶對URL與Web體驗間的關係有無概念?

  • 客戶是否能把瀏覽器工具欄操作與真實的業務場景聯繫起來?

案例:某2C項目

在項目初期,詢問客戶對刷新的看法時,客戶明確表示「所有頁面,刷新一律回首頁」,因為客戶所參考的另一個競品網站就是這麼做的。作為技術團隊,很容易就此得出不必兼容刷新的結論。

然而隨著了解的深入,我們發現客戶有如下需求:

  1. 支持郵件推銷:在發給用戶的郵件里會附帶某個產品的鏈接,打開鏈接希望用戶能看到相關產品頁面

  2. 支持某些頁面保存鏈接:在訂單完成後,會為用戶生成一個含有二維碼的憑證頁作為取貨憑證。用戶可以保存這個頁面的鏈接,並在取貨時(可能是幾天後)直接打開。

以上兩個需求,從技術角度看和支持刷新是等價的:都要求能僅通過URL定位並展示頁面資源。

然而前後溝通的結果卻完全不同,如果技術團隊按照最開始的結果做架構,到最後一定會付出代價。

回過頭看,一開始客戶給出的答案並非剛性需求,背後沒有業務價值,只是因為看到別人這麼做而已,甚至客戶還以為自己主動削減了需求,為開發團隊省了事。而這樣的需求,後期改變的可能性和彈性都是非常大的。

後面兩條,則是包含業務價值的剛性需求,直接影響到客戶利益,幾乎沒有任何討價還價的餘地。

要避免這類問題,最重要的是警惕涉及技術架構的非剛性需求,從多種角度進行確認,引導並挖掘出更多的相關場景。客戶是純業務的,對不同業務場景間的技術共性沒有概念。而如何引導客戶、挖掘真實需求,則是團隊專業性最直接的體現。

在上面這個案例中,當客戶說」不用考慮刷新,一律回首頁「的時候,團隊只要多問一句」那有沒有需要直接用URL打開的頁面呢?「,就很可能引導出更真實的需求。客戶並不知道兩者在技術上等價,開發團隊這時候需要承擔起引導的責任。

小心模態窗

一個典型的模態窗(Modal)如下圖所示:

上下文的黑魔法

它通過模仿窗口的方式,允許用戶在不脫離當前頁面上下文的情況下進行操作。同時它也有一定的阻塞性,可用來控制用戶的操作流程,比如有的電商網站會通過模態窗來引導用戶先選擇所在地區再繼續購物。

之前我們講過Web頁面間的上下文是強隔離的,而模態窗在不脫離上下文的前提下能承載的內容非常可觀,可觀到什麼承度呢?如果把模態窗的長寬設置為填滿當前頁面,那它看起來會和新的頁面沒什麼兩樣!相當於額外100%的內容承載力!

這既提供了設計上的靈活性,也潛藏著風險:既然模態窗可以承載和新頁面一樣多的內容,為什麼不直接用它代替頁面呢?和跳轉到新頁面相比,它還可以保持當前頁的所有狀態:表單內容、滾動位置……

謹防超載

問題在於,如果需要用URL定位到模態窗中的內容,渲染的順序一定是先有模態依附的頁面主體,再有模態窗。URL不僅需要攜帶模態窗的相關信息,連頁面主體的也得帶上。要是模態套模態,套得越深,還原頁面的所需信息越複雜,離URL友好也就越遠

這就像平房和樓房,雖然樓房利用了縱向空間,但想進門一定要從底樓一層層上去。空中樓閣是不存在的,模態窗也不可能脫離自己依附的主體獨立存在。

案例:某後台管理系統

在我剛畢業的時候接觸過一個後台管理系統,由於是小公司,完全沒有Web設計的相關經驗。

那個系統的設計幾乎是完全忽略URL的,一開始是翻頁等重要信息沒有體現在URL中,導致資源無法定位,一旦脫離某個模塊,則在該模塊下的所有操作都得重來。

為了解決這個問題,設計(其實是非設計出身的領導)給出的方案是用模態窗——既然找回上下文困難,不脫離上下文不就行了。

然而這時候的模態就像毒品:想要查看設備的子設備列表?開個模態窗;想看某個子設備的運行狀況?再開個模態窗。就這樣模態窗套模態窗,有礙觀瞻是小事,當某天你意識到Web的世界還有URL這回事的時候,或者客戶報怨說想通過打開保存的鏈接就能看到某個子設備的時候,要改回來已經幾乎不可能了。

要避免類似的問題,至少需要在設計階段(或開發review設計時)注意以下兩點:

1. 控制內容複雜度

模態承載的內容和功能應盡量單一。如果是希望用URL定位的資源,盡量不要設計到模態窗中

例如登錄窗、或者展示商家地圖位置,都是適合模態窗的場景。而像訂單這樣的資源,由於資源間關係複雜,很可能再出現顯示子資源或關聯資源的需求,用模態窗展示的話將來就很容易嵌套。

有種特殊情況是資源本身有URL友好的頁面顯示,為了用戶體驗在其它地方也用模態窗展示該資源的信息,這樣是沒有問題的,因為模態窗並不需要被URL定位

2. 考慮新標籤中打開鏈接

不想脫離當面頁面又想訪問其它資源?其實Web早就想到了,這就是你每天都在使用的新標籤頁打開鏈接

比如訂單列表頁,每個訂單要有訂單詳情的功能入口,而用戶可能是通過複雜的搜索條件以及漫長的滑鼠滾動才看到當前的列表項,並不希望脫離當前頁面。把訂單詳情放模態?缺點前面已經講過了,更好的方式是在新窗口中打開訂單詳情頁,用戶不僅不會脫離當前頁面,甚至可以同時打開多個子頁面,比不能並發的模態高到不知道哪裡去了。

偽頁面

如果說內容超載的問題主要發生在桌面端,那在移動端還有另一個涉及模態窗的問題,個人稱這種設計為偽頁面,如圖所示:

第一個圖是表單頁,點擊箭頭所示的日期,會打開第二張圖所示的日期選擇模態窗。你可以親自上攜程移動端查看並操作。

類似設計移動端非常常見:由於屏幕太小,有些功能本質雖然單一(如城市選擇、日期選擇,本質上都是個選擇框),體現在UI上仍然需要佔滿全屏。但這些功能(特別是表單類操作)又絕對不能脫離宿主頁單獨存在,最終只能選擇用模態窗來處理。

這就產生了矛盾:全屏顯示的功能很容易給用戶「這是一個頁面」的錯覺,而實際上它並不是。這個矛盾在面對刷新後退前進等操作時尤其明顯:

  • 當模態窗打開的時候,點擊刷新,要不要重新打開模態窗?

  • 當模態窗打開的時候,瀏覽器後退是關閉模態窗還是後退到真實的上一頁?

  • 如果後退可以關閉模態窗,那前進是否可以打開它?

感興趣的朋友不妨在剛才攜程的鏈接上將以上幾點操作一番,親身感受。

這些細節是需求分析和設計階段很難考慮全面的,往往遇到問題了才開始頭痛醫頭腳痛醫腳,最終無論對用戶體驗還是技術團隊,都很容易造成傷害。

解決方案?

個人對此並沒有十全十美的解決方案:全屏顯示的需求與功能並非頁面的本質,矛盾的兩頭都無法輕易解決。假如本文能讓你在正式開發前就想到這個問題,對我來說已經是功德一件了。

不過也並非完全沒有辦法,既佔滿屏幕,又能暗示背景上下文的設計從iOS 7就開始流行了——沒錯,就是毛玻璃效果

粗略地改了一下攜程的日期選擇界面樣式,如下圖所示:

通過毛玻璃效果消除了彈出層是新頁面的錯覺後,用戶就不容易對刷新、後退等操作出現錯誤的期望。

載入資源之What - When - How

要做到URL友好,一個必要條件就是頁面能自行載入所有需要的外部資源

What

什麼是外部資源?不就是伺服器端數據嗎?對,但是不全對。

隨著介面的無狀態化,Web App會保存一些原本在伺服器端存在的狀態,例如登錄信息。對每個頁面而言,這些跨頁面共享的狀態也應該視作頁面的外部資源。

這聽起來有點奇怪:頁面就是Web App的一部分,卻要把Web App的狀態視作外部資源?

其實很好理解,核心仍然在於:組成Web App的每個頁面,其上下文是隔離的。

還記得文章開頭的基本差異第三條吧?以用戶登錄信息為例,即使你把信息放進了localStorage以保證刷新後可訪問,也擋不住用戶在其它瀏覽器訪問同一個鏈接,甚至乾脆把鏈接分享給別人——這意味著你不能假定這些數據的可靠性,對單個頁面而言,Web App的狀態與伺服器資源一樣是不可知的黑盒

因此,即使你可以像訪問內存一樣用同步代碼訪問localStorage,我仍然建議你把Web App的狀態管理與API請求放到相同的層級進行考量,這樣能更好地反映對問題本質的抽象。

插播技術觀點一則:localStorage規範同步IO更像是歷史遺留問題。未來一定會被非同步存取的標準或第三方庫取代(火狐就搞了一個非同步的localforage),將應用狀態與API同等對待的向前兼容性更優。

When

這個問題看起來沒什麼好說的——當然是在頁面載入時。

但如果需求方給你的是App的設計稿,那請務必注意:App的設計有可能是在上個頁面載入下一頁的數據,再根據結果決定是否跳轉到下個頁面;而Web由於URL的獨立可訪問性,通常是先跳轉到目標頁面再載入數據

這帶來的第一個差異就是Loading進度的顯示問題:App可以在上一個頁面顯示Loading,而Web則不建議這麼做。原因很簡單,頁面自己能載入資源,上個頁面再幫它載入,結果一定是要麼浪費資源,要麼增加複雜度。

目前多數App的設計和Web一樣是先跳轉再載入,然而先載入再跳轉的設計也是存在的,比如下圖國外某航空公司App:

點擊「Find flights」,會出現如圖所示的小菊花,等到載入結束才跳離當前頁面。

而攜程移動版(無論Web還是App)就是標準的頁面自行顯示loading:

How about failed?

第二個差異則是如何處理載入失敗。在App端,如果由上一頁面載入下個頁面的數據,則錯誤的處理(例如彈窗)也會在上個頁面顯示,下游頁面從設計上基本不會考慮載入失敗時的渲染問題。

上圖是點擊"Find flights"請求失敗後,頁面沒有跳轉,直接在當前頁提示錯誤。

作為對比,攜程的移動端如圖:

從以上截圖也可以看出來,國外航空App的設計如果做成網站,是很難保證URL友好的。稍微總結一下的話就是:

Web由於URL的獨立可訪問,每個頁面都必須考慮載入中、載入失敗的時候如何顯示,如果設計給你的只有成功的場景,請馬上找他重新核對需求。

結語

苟利老闆虧盈以,豈因禍福避趨之

工程即妥協,有時候為了更好的用戶體驗,上述任何一點都可能犧牲。本文的目的不是形成教條,而是幫助你在權衡利弊時能想得更加清楚。

更何況,無論是Web還是App,都在高速發展並相互影響。瀏覽器賦予開發者更加豐富的、接近App的介面,而App也在借鑒Web的資源定位機制。

另一方面,本文所討論的其實是處於設計與技術間的灰色地帶,甚至需要工程師去挑戰和影響設計。這是因為團隊分工程度越高,這些灰色地帶的銜接就越可能導致項目失敗。個人非常鼓勵程序員,特別是前端工程師去思考業務和設計,乃至於輸出影響力。前端開發的本質是data -> design的函數,只有深入地理解輸入與輸出,甚至主動出擊修正偏差,才能交付真正的價值。


推薦閱讀:

如何通過canvas進行簡單的圖像識別?
10本學習前端必看書籍,讓你豁然開朗
汪汪汪,抓緊啦,年前最後一期周刊來啦
前端日刊-2018.02.03

TAG:Web開發 | 前端開發 | 前端工程師 |