當前位置:
首頁 > 最新 > 輕量級高性能Hybrid框架VasSonic秒開實現解析

輕量級高性能Hybrid框架VasSonic秒開實現解析

H5很重要,H5很重要,H5很重要,重要的事情要說三遍。VasSonic是騰訊開源的解決H5首屏渲染痛點的開源項目,本文通過解讀代碼來學習WebView的優化思路。

H5的優劣

H5的優勢很明顯,跨平台、迭代快、開發體驗好。H5的劣勢同樣明顯,載入慢,用戶體驗差。業內大牛想盡各種方法來彌補H5的劣勢,初級使用緩存、預載入等常用方案,高級如Hybrid、ReactNative、Weex等H5的進階解決方案。VasSonic專註於H5的秒開,使用的也是我們常見的性能優化方案。本文嘗試了解VasSonic是如何用常見的手段將性能優化做到極致的。

VasSonic解決什麼問題

關於WebView為什麼打開慢、載入慢,業界已經有很多分析了,結論也是比較一致的,推薦美團點評技術團隊的WebView性能、體驗分析與優化,騰訊關於VasSonic的官方文章也有相關說明。

WebView載入慢的問題主要集中在如下三個階段:

WebView打開

頁面資源載入

數據更新導致頁面刷新

VasSonic的優化都是為了加速上述三個階段,其經驗可以總結為六個方面。

WebView池:預先初始化WebView

靜態直出:服務端拉取數據渲染完畢後,通過CDN加速訪問

離線預推:離線包方案

並行加速:WebView的打開和資源的請求並行

動態緩存:動態頁面緩存在客戶端,用戶下次打開的時候先打開緩存頁面,然後再刷新

動靜分離:為了提升體驗,將頁面分為靜態模板和動態數據,實現局部刷新

預載入:在打開頁面之前將資源數據都準備好,提升頁面打開的速度

可以說是非常全面了,具體細節可以參考騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開!。

上述優化的核心技術主要涉及幾個方面:

WebView池

緩存設計

資源請求和WebView分離設計

動靜分離設計

下面結合代碼來看看VasSonic是如何實現這些優化點的。

準備工作:從github VasSonic clone最新代碼,打開sonic-iOS目錄下的SonicSample。

WebView池

UIWebView並不是開源的,想要通過修改源碼來提升打開速度是不太現實的。VasSonic採用的方案是預先創建WebView池。在應用啟動或者空閑的時候預先創建空的WebView,等真正要用的時候直接從池中獲取WebView。

Demo中只是簡單的預載入了一次WebView,通過創建空的WebView,可以預先啟動Web線程,完成WebView的一些全局性的初始化工作,對二次創建WebView能有數百毫秒的提升。在實際應用中,我們可以採用WebView池的方式來進一步提升打開速度。

緩存設計緩存類型

VasSonic將緩存的類型分成了四種,他們分別是模板、頁面、數據和配置。

將模板和數據分離是實現動靜分離的核心技術,模板和數據是從頁面數據中自動分離出來的,緩存頁面數據的時候,SonicCache會調用 分割模板和數據,代碼實現如下:

還是以Demo為例看split的結果。

除了頁面、模板、數據類型的緩存外,還有一個非常重要的緩存是config。先看下config的生成。

ETag大家應該是比較清楚的,在HTTP的緩存設計中有重要作用,當服務端發現客戶端請求帶的資源的ETag和服務端一樣的話,就不會返回完整的資源內容了,節省時間和帶寬,templateTag也是類似的,當templateTag不一樣的時候,服務端才會更新模板。

簡而言之,Config就是保存了這次請求頭中的一些重要信息,留待下次請求的時候發還給服務端做優化。

緩存Key

說完緩存類型,必須要說一下緩存的key,這個非常重要。首次請求會調用 緩存數據。入參有htmlData、header和url,前面已經分析htmlData是需要緩存的頁面數據,htmlData會被存成html、template和dynamicData三種類型,headers前面也提到了是緩存成config,那這個url的作用就是生成緩存的key。

首先根據url生成sessionID,然後再將sessionID和特定的 實例綁定。這裡我們先說明每個固定url生成的sessionID是一樣的,這才能讓我們在相同的url請求的情況下使用緩存,具體的url生成sessionID的規則在 章節詳細說明。

SonicCacheItem

每個緩存Key,也就是根據url生成的sessionID都會對應一個SonicCacheItem的實例,用來緩存所有的數據。SonicCacheItem也就是一個緩存的數據結構,包含htmlData、templateString、dynamicData、diffData等等。

SonicSession

講緩存的時候我們提到過作為緩存Key的sessionID,每個sessionID關聯了一個緩存對象SonicCacheItem,同時也關聯了一次URL請求,VasSonic將這個請求抽象為SonicSession。SonicSession在VasSonic的設計裡面非常關鍵。其將資源的請求和WebView脫離開來,有了SonicSession,結合SonicCache,我們就可以不依賴WebView去做資源的請求,這樣就可以實現WebView打開和資源載入並行、資源預載入等加速方案。

SessionID

每個sessionID唯一指定了一個SonicSession,sessionID的生成規則如下:

每個url都能唯一的確定一個sessionID,需要注意的是,算md5的時候並不是直接拿請求的url來算的,而是先經過了 的函數的處理。理解 對url的處理有助於我們了解VasSonic的session管理機制。

其實 做的事情比較簡單。

對於一般的url來說, 會只保留scheme、host和path,url其他部分的改變不會創建新的session

新增了 參數, 裡面指定的query參數不同會創建新的session。

舉栗說明:

的代碼也比較簡單,這裡就不貼了,有興趣的同學可以參考這裡sonicUrl實現。

自定義請求頭

之前提到過SonicCache的一種緩存類型是Config,SonicSession在初始化時候會根據緩存的Config更新請求頭,以便服務端根據這些信息做相應的優化。

除了會添加自定義的請求頭參數,以及將緩存的config加到請求頭裡面外,在每次發起請求之前,都會同步cookies,這樣就可以保持狀態了,比如登陸狀態等等。

做了上面這些工作,我們可以抓包看最終一個請求會長成什麼樣子。通過對Demo中LOAD WITH SONIC抓包發現請求頭中帶了sonic-load-type、template-tag、sonic-sdk-version等等,服務端正是基於這些參數做了優化。

網路連接

VasSonic默認提供了基於URLSession的 來發起請求和處理響應。 做的事情並不多,主要實現了兩個介面,並提供 定義的網路回調介面供session處理。

如果需要在發起請求和處理響應階段做一些自定義的動作的話,比如實現離線包方案等等,就可以自定義繼承於SonicConnection的Connection對象,在回調 方法之前做些處理。

註冊自定義的Connection對象使用如下的方法,可以同時註冊多個,通過實現 來決定使用哪個Connection。

值得注意的是,SonicConnection的所有介面設計都類似NSURLProtocol協議,但他並不繼承自 ,原因在本文最後WebView請求攔截部分會有提到。

緩存處理

SonicSession根據請求響應頭中 返回的存儲策略的不一樣會有不同的處理,Sonic定義了如下幾種離線存儲的策略。

當SonicSession在發起請求之後需要處理本地有緩存和沒有緩存兩種情況。

沒有緩存的情況

沒有緩存,首次載入的情況下根據策略的處理方式也比較簡單,沒啥好說的,直接上代碼。

有緩存的情況

有緩存的情況相對來說要複雜一些,需要處理模板更新和數據更新兩種不同的情況。

模板變化是直接調用了 來更新緩存,可見模板變化會導致之前的緩存都失效。

數據變化則是調用 來更新緩存,該函數會將本地的緩存和服務端返回的數據做個diff,然後返回給前端更新界面。

攔截WebView請求

現在SonicSession結合SonicCache能獨立高效處理URL請求,那麼如何使用SonicSession來接管WebView的請求呢?iOS下所有的URL請求都是走URL Loading System的,攔截WebView的請求只需要自定義實現 協議就可以了。

因為NSURLProtocol會攔截所有的請求,那如何只針對Sonic WebView發起的請求實現攔截呢?可以通過 來實現,只有請求頭中帶 的才會被攔截。

當系統發起請求的時候,Sonic並沒有真正的發起請求,而是用SessionID註冊了回調,讓SonicSession在恰當的時候調動回調。

接下來我們看看SonicSession都是在什麼時機調用回調函數的,首次載入、預載入和完全緩存狀態是不一樣的。

首次載入的時候,根據網路的實際回調時機調用即可,代碼如下:

有預載入的情況下,根據預載入的情況構造需要回調的動作,代碼如下:

完全緩存的情況下,構造完整的回調動作,代碼如下:

這樣業務使用者只需要正常的實現 的協議就可以了,不需要關心回調是來自真正的網路連接、還是來自預載入,或者是完全的緩存,所有的緩存優化就都能被封裝在SonicSession裡面了。

這裡有一點需要說明的是SonicURLProtocol和SonicConnection是不一樣的,雖然SonicConnection模仿了NSURLProtocol的介面,但是其父類是NSObject。SonicURLProtocol最大的功能是實現WebView的請求攔截,而SonicConnection則是SonicSession的網路請求處理類。

頁面刷新

經過上面的描述,我們基本已經將整個流程都串起來了。

整個流程最後的WebView頁面展示,也是非常重要的一塊優化。

當請求結束的時候,SonicSession會根據是否是首次載入分別調用 和 ,這兩個函數除了對緩存的不同處理外,還有一個非常重要的區別:前者調用了 ,後者則不會。也就是說前者會將請求結束的結果告訴WebView,而後者不會,導致的結果就是前者會刷新頁面,而後者不會。但是 中有這麼一段代碼。

如果有 ,那麼這個回調是會被調用的,參數是經過diff之後的數據,看到這裡應該同學都明白了,這就是局部刷新的實現機制。

Sonic給JS暴露一個方法叫 ,JS只要設置該回調,最終就是設置了 。

這部分的js相關實現在sonic.js中,有興趣的同學可以自行翻看js源碼。Demo中的更新邏輯如下:

結論

總結來看VasSonic並不是與眾不同的新技術,但是其對HTML、客戶端WebView有著深入的了解,通過司空見慣的一些技術的極致搭配和使用,極大的提升了WebView的性能。仔細研究SonicSession和SonicCache的實現對於了解VasSonic的設計思想非常重要。最後感謝騰訊團隊給開源界帶來這麼優秀的WebView框架。

參考文獻

騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開!

WebView性能、體驗分析與優化

URL Loading System

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 中意明安 的精彩文章:

TAG:中意明安 |