淘寶承接頁是如何實現(xiàn)秒開的
前言
用戶承接頁,是承載上游的落地頁,其核心職能是承接流量、轉化用戶。對用戶增長業(yè)務來說,如何讓用戶更快看到頁面,是影響用戶決策、決定拉新成功的關鍵。用增承接頁的目標用戶是手淘低活用戶,這部分人的手機設備中低端占比90%以上,網(wǎng)絡條件也不穩(wěn)定,這對于我們承接頁的性能、體驗提出了更高的要求。
業(yè)務背景
我們業(yè)務目標用戶是淘寶潛客人群,業(yè)務上以定向權益配合特定的貨品池,打造低價心智來吸引用戶。一般的承接頁形式如下:

承接頁一般都會紅包搭配貨品,這里有2個比較重要的邏輯:紅包直塞、補貼價計算。
紅包直塞:用戶訪問頁面的時候,就判斷是否是目標人群,如果是目標人群,直接發(fā)放紅包權益。
補貼價計算:貨品模塊根據(jù)紅包發(fā)放狀態(tài)展示抵扣后的價格,讓用戶在前臺看到最低的價格。
由于承接頁每個模塊都要處理定向的權益、貨品,導致模塊請求都是定制化的,而且為了得到最精確的補貼價,模塊請求之間還會有串行邏輯,貨品模塊需要等待紅包模塊請求成功后發(fā)起。
如何滿足業(yè)務的定制需求,讓用戶擁有更好的體驗,幫助業(yè)務成長,我們針對如何提升承接頁的性能和體驗,開啟了專項優(yōu)化之路。
承接頁的秒開優(yōu)化
首先來看未優(yōu)化前的承接頁,肉眼可見的“慢”,原始頁面性能數(shù)據(jù)如下:頁面首屏可視時間:低端機6.6s、中端機4.2s、高端機2.8s,平均首屏可視時間4.9s。
分析performance,導致頁面變慢的原因主要是:
總體js過大,業(yè)務js 200k,加上基礎js,總大小約有400k 接口串行,先請求紅包后請求貨品,導致請求時間變長 外部條件惡劣,用戶機型差、網(wǎng)絡差的情況下,請求時間不可控
H5頁面從加載到首屏可視,主要經(jīng)歷了webview初始化 - 主文檔加載 - 資源加載 - 數(shù)據(jù)請求 - 業(yè)務內容渲染幾個部分,我們針對每個步驟影響對首屏可視時間的影響,進行了:中心化接口改造、數(shù)據(jù)預加載、靜態(tài)化SSR的優(yōu)化,最終實現(xiàn)了承接頁的秒開,低端機首屏可視時間0.9s,高端機0.8s。
| 機型 | 方案 | 頁面可視時間 |
|---|---|---|
| 低端機 | CSR(未優(yōu)化) | 6.6s |
| 靜態(tài)化SSR | 0.9s(優(yōu)化5.7s) | |
| 高端機 | CSR(未優(yōu)化) | 2.8s |
| 靜態(tài)化SSR | 0.8s(優(yōu)化2s) |
(低端機 - CSR vs SSR)
承接頁優(yōu)化過程
中心化接口改造
最初的承接頁,每個模塊單獨定制發(fā)請求,請求串行,頁面渲染鏈路如下:

為了對用戶做定向權益和貨品,承接頁會進行紅包直塞、補貼價計算邏輯,原本的執(zhí)行邏輯交給前端來控制,通過紅包模塊請求完畢后發(fā)送事件告訴其他模塊發(fā)起請求,在用戶網(wǎng)絡條件不穩(wěn)定的情況下,首屏可見時間不可控。
所以我們進行了中心化接口的改造,將模塊中定制的請求邏輯抽離,將數(shù)據(jù)請求合并成一個。同時服務端改造,紅包直塞和補貼計算的串行邏輯在服務端處理,前端模塊通過一個動態(tài)加載器模塊請求頁面數(shù)據(jù)并分發(fā)給各個模塊。改造后,模塊只需要根據(jù)拿到的數(shù)據(jù)進行處理,讓模塊開發(fā)變成:UI+數(shù)據(jù)的簡單模式,每個模塊的平均開始時間,也從2人日減少到0.5人日。
中心化接口后的頁面渲染鏈路如下:

數(shù)據(jù)預加載
數(shù)據(jù)預加載,也叫prefetch,是淘寶這邊結合客戶端的優(yōu)化手段。中心化接口將首屏接口請求減少到1個,為開啟數(shù)據(jù)預加載做好了準備。
簡單來說,數(shù)據(jù)預加載就是淘寶客戶端,根據(jù)下發(fā)的配置文件,來判斷當前頁面需不需要提前發(fā)頁面請求。如果命中配置文件的相關配置,在用戶點擊進入目標頁面時,webview初始化階段就發(fā)起頁面請求,當頁面接口請求真實發(fā)起時,可以直接使用提前請求的結果,從而減少接口請求占用的時間,頁面渲染過程如下:

一般來說提前首屏的接口請求,大約可以節(jié)省300~400ms的時間,如果是低端機,收益會更高。這是開啟了數(shù)據(jù)預加載后的對比視頻:
(低端機y67 - CSR vs prefetch)
數(shù)據(jù)預加載雖然可以提前發(fā)出請求,但在傳統(tǒng)的CSR鏈路中,首屏時間還是比較長,主要是因為基本JS+模塊JS這部分資源加載還是很耗時,對于低端機更是明顯。另外,該能力依賴客戶端提供能力,如果是用戶喚端入淘的場景,數(shù)據(jù)預加載的命中率很低,能提供的幫助也比較有限。
靜態(tài)化SSR
傳統(tǒng)的CSR頁面,如果需要優(yōu)化,資源的緩存和提前請求基本都需要依賴客戶端配合,而用戶增長業(yè)務的場景包含端外、端內兩部分,這使得很多客戶端的優(yōu)化手段用不上,這也是我們承接的一個難點。
如何在資源加載和請求發(fā)出前,就讓用戶看到首屏呢?我們想到了利用SSR(服務端渲染)。常規(guī)的SSR方案,是將頁面的渲染工作放到了Server端,在文檔請求中返回渲染好的HTML,但這個方案成本很高:
改造成本高,承接頁使用的是多是搭建鏈路,改成服務端渲染需要修改原本的模塊機制,導致頁面渲染架構需要修改 服務器成本高,由于用戶請求url的時候,就會發(fā)起對服務端的渲染請求,針對大流量來說,服務器成本不得不考慮;另外服務端渲染失敗的情況,會導致直接出現(xiàn)白屏,缺少兜底能力 無法結合客戶端優(yōu)化,渲染過程放在了服務端,導致無法結合客戶端做優(yōu)化
經(jīng)過我們的技術調研,最終在淘寶承接頁落地了「靜態(tài)化SSR」方案。利用CDN緩存做靜態(tài)化,當命中緩存直接返回SSR HTML,如不命中則通過SSR FaaS服務,重新渲染最新的SSR HTML,并寫入CDN緩存。大致流程如下:

一方面靜態(tài)化SSR利用了CDN緩存,就近原則,可以讓它獲得比常規(guī)SSR更好的性能;另一方面,大部分人命中CDN的情況下,對服務端(SSR FaaS)的壓力相對較小。當然它也有缺點,因為緩存的原因,導致它基本支持不了個性化。
不巧的是,用增承接頁主要是定向權益、貨品等個性化內容,所以我們在FaaS服務上做了匿名緩存,也就是只緩存無個性化內容,保證SSR鏈路的CDN緩存是通用數(shù)據(jù)。當用戶的頁面JS加載后,我們發(fā)出真實請求,替換頁面緩存的內容。
(低端機y67 - CSR vs prefetch vs SSR)
靜態(tài)化SSR動畫數(shù)據(jù)
靜態(tài)化SSR方案,當用戶緩存內容和真實內容有區(qū)別的時候,會有比較明顯的數(shù)據(jù)刷新的過程,這對于用戶體驗來說,“不是不能用,但是不夠好”。有沒有辦法來優(yōu)化從匿名緩存到個性化數(shù)據(jù)的過程呢?我們提出了靜態(tài)化SSR動畫數(shù)據(jù)。
所謂靜態(tài)化SSR動畫數(shù)據(jù),是指將用戶的數(shù)據(jù)切換過程中的直接刷新DOM的過程,改成設計感的過渡動畫。這個過程,可以由前端和設計師參數(shù),將“bug”變成“feature”。
天降紅包動畫:數(shù)據(jù)變化出現(xiàn)突然抖動出現(xiàn)紅包模塊,給人突兀的感覺,可以設計成紅包模塊出現(xiàn)的時候是一個從上而下彈出,并最終收攏到紅包模塊位置的前端動畫,給人一種“天上掉紅包”的感覺 商品換一換動畫:原本的數(shù)據(jù)變化會把緩存的商品替換掉,讓人有bug的感覺,可以設計成真實請求商品數(shù)據(jù)返回時,通過漸入漸出、上下滑動的動畫形式,給出“換一換商品”的氛圍感,如果更精細點,同個商品因為有權益添加后價格降低,可以做成價格滾動動畫,突然“降價”的感覺
我們在與業(yè)務溝通后,現(xiàn)階段暫時采用了比較簡單的過渡動畫,效果如下,播放速度做了0.5倍處理:
結尾
前端性能優(yōu)化是一個老生長談的問題,也是一場持久戰(zhàn)。在這個過程中,我們需要深入理解頁面架構和業(yè)務邏輯,否則很難找出設計不足的地方,與此同時,我們也要遵循以下幾個原則。
1、依據(jù)數(shù)據(jù)而不是憑空猜測:頁面有性能問題,我們可以通過performance、監(jiān)控、日志等手段,找出具體導致頁面很慢的原因,從而對癥下藥。
2、忌過度優(yōu)化,綜合考慮:性能優(yōu)化方案需要綜合考慮開發(fā)成本、服務器成本,需要做衡量,不要過度優(yōu)化。
3、性能優(yōu)化要與業(yè)務結合:我們技術優(yōu)化的目標是為了讓業(yè)務更好,選擇適合業(yè)務、提升業(yè)務的方案才是一個好方案。
內推社群
我組建了一個氛圍特別好的騰訊內推社群,如果你對加入騰訊感興趣的話(后續(xù)有計劃也可以),我們可以一起進行面試相關的答疑、聊聊面試的故事、并且在你準備好的時候隨時幫你內推。下方加 winty 好友回復「面試」即可。
