QQ空間前端工程

極牛發表於2016-09-29

極牛技術實踐分享活動

極牛技術實踐分享系列活動是極牛聯合頂級VC、技術專家,為企業、技術人提供的一種系統的線上技術分享活動。

每期不同的技術主題,和行業專家深度探討,專注解決技術實踐難點,推動技術創新,每兩週的週三20點正式開課。歡迎各個機構、企業、行業專家、技術人報名參加

嘉賓介紹
崔 進 QQ空間的前端工程師

本次主題
QQ空間的hybrid頁面首屏優化方案webso,因為活動頁面、運營頁面的需要,亦或者客戶端開發週期長,需要採用H5的技術方案,越來越多的H5頁面內嵌在客戶端裡了, 即所謂hybrid形式。
QQ空間如何優化hybrid頁面
把H5頁面內嵌在QQ空間客戶端裡面,是一個開發重點轉型的問題,也是面臨的新的優化課題。
hybrid頁面主要體現在兩個客戶端:QQ空間客戶端和手Q客戶端

當初面臨的主要體驗問題是:

  1. 客戶端的webview啟動太慢,尤其是android上

  2. 頁面載入有白屏的過程
    所以我們的精力也主要集中於來解決這兩個問題。

在解決這兩個問題時,主要參考的是現有成熟的技術方案,比如native的客戶端是怎麼保證體驗的,native客戶端會快取使用者上一次訪問的資料,下一次進來的時候會先顯示上一次的內容,再更新成新的內容。那我們是不是也可以採用類似的方案呢?

首先分析了我們當時的現狀:

  1. 我們的H5頁面都是通過自研的node.js server直出的,這意味著頁面輸出層我們是可以控制的。

  2. 我們的H5頁面是內嵌在QQ空間客戶端和手Q客戶端的,那這兩個客戶端都是可以配合做相應的優化修改的。

所以,我們最終有了webso的技術方案:

  1. 客戶端快取node.js直出的H5頁面,並且node.js通過自定義的響應頭來控制客戶端的快取;

  2. 客戶端將初始化webview和載入頁面內容並行進行,並通過客戶端的長連線通道來載入頁面內容,來提升弱網路下的體驗

圖片描述

上圖就是我們對初始化webview的流程進行優化後帶來的好處。
這樣就大大弱化了webview啟動慢的問題,如果客戶端長連線通過夠快,甚至可以達到webview啟動完成,頁面就可以顯示出來了,基本可以媲美客戶端的體驗了。
webso方案核心內容
webso方案包含的主要幾個核心內容是:

  1. 離線快取
    首先,webso借鑑的就

是客戶端的快取方案,我們也是講H5的直出內容快取在客戶端了,但是流程和細節稍有差異。
圖片描述

這是webso方案的主要流程,首先使用者點選客戶端上的某個hybrid頁面的入口按鈕後,客戶端會開始初始化webview,並同時通過客戶端的wns元件去載入本地快取。(wns是QQ空間客戶端的基礎網路元件)wns在載入到當前頁面的本地快取後會將頁面塞到webview裡面去顯示(如果webview還沒有初始化完成,就等待webview初始化完成),同時會計算快取頁面的md5,在通過wns的長連線通道去載入新的H5頁面內容時,會將md5放置在請求頭的e-tag頭上。
後端node.js在收到請求到,會像處理正常的http請求一樣工作,只是在最後一步的時候會將要返回給客戶端的html頁面內容交給node.js的一個gzipHttp模組。
gzipHttp模組會根據請求頭裡的e-tag裡的值和當前需要返回給客戶端的html頁面的md5進行對比,如果一致,則會在響應頭上放置一個自定義的響應頭cache-offline: store,並返回304,如果不一致則會在響應頭上設定cache-offline: true.

cache-offline有如下幾種取值,以及客戶端的處理方式。
false: 響應內容不進入離線,重新整理webview,並清掉本地快取。
true: 響應內容進入離線,重新整理webview.
store: 響應內容進入離線,如果weview已經有內容在顯示則不重新整理webview.
無: 作用與false一樣。
這樣就達到了我們的目的:webview啟動後用快取的頁面內容展示給使用者,wns元件並行載入到最新的H5頁面,更新webview的展示並更新客戶端本地的快取。

2. 增量更新
上面的流程中有提到,node.js在返回html內容給客戶端的時候有兩種可能:304和完整的頁面內容。
增量更新主要目的就是優化返回完整頁面內容的體驗,有的時候,有的頁面體積比較大,導致載入的時候比較慢。同時我們調研發現,大部分頁面都是有部分頁面內容是不怎麼變化的,比如head部分,內聯的樣式,內聯的指令碼等等,並且這部分內容佔比並不小,佔有頁面體驗一半左右。
所以如果我們將頁面的這部分不怎麼變化的部分跟經常變化的部分分開,然後對不變的部分進行增量更新,效果是不是會更好呢?
最終的效果是肯定的,我們先來看下增量更新具體是怎麼實現的:
跟離線快取一樣,我們需要控制客戶端的快取,並且客戶端需要向後端node.js提供本地的快取資訊。不同的是,後端node.js需要將頁面拆分成動態內容和靜態內容兩部分,而客戶端則需要將靜態內容和動態內容進行合併。
圖片描述

這是我們在離線快取的基礎上增加了增量更新的特性後的流程圖。增量更新對比的處理仍然就交給gzipHttp模組處理。
首先客戶端會通過在請求頭中設定accept-diff來開始後端改客戶端是否支援增量更新,通過會通過template-tag將本地快取的靜態內容的md5帶給後端。
後端的gzipHttp模組在返回html內容時會先通過e-tag和整個頁面的html md5對比頁面是否有變更,然後對需要返回給客戶端的html進行拆分,然後對拆出來的靜態內容進行md5,然後對比請求頭裡的template-tag值,如果一致,說明靜態內容沒有變更,如果不一致則說明客戶端的靜態內容需要更新。
對靜態內容需要更新時,後端server會取出與template-tag一致的快取,然後與剛拆出來的靜態內容進行二進位制對比,並拿到增量內容,然後將增量內容的base64與動態內容一起返回給客戶端。
客戶端則完全是一個逆向的過程了,先將增量內容更新到本地的靜態內容上,然後將最新的靜態內容和動態內容進行合併。
這是一個典型的支援增量更新的頁面 :

圖片描述
中間的wnsdiff-body的一組標籤就是開始後端node.js從這裡來拆分靜態內容和動態內容。

  1. 預載入
    預載入,是屬於錦上添花的點。

我們整個webso的方案其實都是依賴於客戶端本地的快取,如果沒有本地的快取,體驗還是提升不上去。
所以,我們提出了預載入的方案,這個主要是客戶端提供的能力。
主要的價值就是:

  1. 可以在使用者訪問頁面前就通過前置的頁面來預載入,提高快取覆蓋率

  2. hybrid頁面可以通過客戶端介面來控制本地快取,而不是完全後後端node.js來控制快取。

圖片描述

可以看出介面其實很簡單,就是告訴客戶端需要快取的頁面url,以及是否需要先判斷本地快取的選項。
通過這個介面,我們可以滿足如下幾個場景需求:
前置頁面預載入其他頁面或活動頁
修改後動態更新快取
清除本地快取
以上就是webso方案的主要內容。

Q&A
Q1:快取H5是怎麼實現的,因為除了html以外還有js css 圖片等資源,這些是否都需要一併快取?
A1:webso方案主要關注的還是H5直出的html內容部分,其他靜態資源還是依賴瀏覽器快取,但是目前客戶端也提供了基於客戶端快取的技術方案,與webso類似。
Q2: 是選用hybird方式,還是一步到位選用react native方式?
A2:其實QQ空間也面臨這樣的問題,但是目前來看,rn的開發效率還是沒有達到期望的效率,QQ空間也在實驗階段,目前hybrid頁面還是主力。
Q3: 有哪些常用的hybird框架?
A3:其實,空間的hybrid頁面會盡量追求簡單純粹一點,功能會相對單一一點,作為客戶端的一個補充,不會用比較重的框架,一般也是用一些基礎元件或工具庫就可以解決。
Q4: 選用hybird方式後,是招新同學開始hybird開發,還是從現有app開發轉過去?你們是怎麼做的?
A4: 我們hybrid的頁面開發是有原來的PC web前端開發來承擔的, app會提供基礎介面,hybrid頁面的開發和客戶端的開發是解耦的。
*此分享由QQ空間的崔進在極牛線上技術分享群裡所分享

【下期重磅預告】
<主題>
【React+Redux 效能優化實踐 】
<講師>
劉華清,GrowingIO 核心工程師,負責 GrowingIO 整個前端工程的搭建。
<大綱>
1.What is React + Redux?
2.Why do we use React + Redux?
3.How do we use React + Redux?
4.Solution for higher performance

有意加入的技術朋友,請在極牛公眾號(ji-niu)裡回覆“技術分享”

相關文章