webview應(yīng)用首屏優(yōu)化

這里首屏優(yōu)化是指用戶安裝應(yīng)用后,首次打開應(yīng)用消耗時間的優(yōu)化。 其中比較耗時的一點是首次打開webview應(yīng)用,加載靜態(tài)資源。 優(yōu)化思路是,首次打開應(yīng)用,使用客戶端攔截請求,返回本地文件。以后的請求優(yōu)化由ServiceWorker接管。

webview請求過程

一個靜態(tài)資源的請求,分為以下4個過程按順序執(zhí)行

  1. ServiceWorker
  2. http緩存
  3. App應(yīng)用攔截
  4. 網(wǎng)絡(luò)服務(wù)

注意:sw.js文件的規(guī)則不在此列,sw.js文件好像始終請求網(wǎng)絡(luò)

image.png

有以下幾點需要注意:

第一步 ServiceWorker 必須是安裝好的

第一次進(jìn)入應(yīng)用,ServiceWorker并未安裝,所以會被直接跳過,進(jìn)入http緩存

ServiceWorker的請求,也會先查看http緩存

就是說,從ServiceWorker發(fā)出的請求,也會遵循h(huán)ttp緩存規(guī)范,http緩存中有的文件,會被直接返回給ServiceWorker

只有ServiceWoker和http緩存都未命中,才會被App應(yīng)用攔截

App應(yīng)用攔截的限制

App應(yīng)用攔截webview請求,是通過復(fù)寫shouldInterceptRequest方法實現(xiàn)

@TargetApi(VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view,
                    WebResourceRequest request) {

經(jīng)過測試,他有限制:
只會攔截html請求和寫在html中的資源請求(即通過<link><script>等直接加載的資源。通過js請求的資源都不會被攔截,例如ajax請求、main.js文件中請求的sw.js、ServiceWorker中發(fā)起的請求)。沒有深入研究測試,理解也可能有誤。


優(yōu)化思路

App應(yīng)用安裝時候,將首屏所需靜態(tài)資源下載到本地文件中

首屏

首次打開應(yīng)用,ServiceWorker未安裝,也沒有任何http緩存,所以所有請求會直接被App應(yīng)用攔截,在這一步返回本地文件,達(dá)到秒開效果。
同時不攔截sw.js文件(ServiceWorker的注冊文件),去網(wǎng)絡(luò)服務(wù)器請求sw.js文件進(jìn)行注冊安裝。

第二次打開

因為在首屏打開應(yīng)用同時,sw.js也同時注冊安裝好了,同時通過cacheAll,也在ServiceWorker中緩存了最新的網(wǎng)絡(luò)服務(wù)器中文件。
所以第二次打開,所有的請求,都可以被ServiceWorker進(jìn)行攔截處理,根據(jù)上面的注意事項(通過js請求的資源都不會被App應(yīng)用攔截),所以第二次及之后的請求,基本就和App應(yīng)用攔截?zé)o關(guān)了。

ServiceWoker緩存的更新

  1. 在每次請求時候,返回ServiceWorker緩存同時,請求最新文件(副作用,在每次頁面版本發(fā)生改變時候,第二次進(jìn)入應(yīng)用會卡,因為第一次返回ServiceWorker緩存,不會卡,但是請求到了新的index.html,第二次進(jìn)入應(yīng)用,根據(jù)新的index.html,發(fā)生變化的靜態(tài)資源,都會從網(wǎng)絡(luò)請求等待,第三次進(jìn)入應(yīng)用才不會卡)
  2. 更新sw.js文件時候,更新所有靜態(tài)資源文件(未實踐,應(yīng)該可行,且無上面的副作用)

ServiceWoker文件sw.js自身的更新

對sw.js不設(shè)置緩存,每次都到網(wǎng)絡(luò)請求最新sw.js文件即可(對于如何安裝更新sw.js,本文不探討)

具體實現(xiàn)

首屏加載

首屏資源請求流程
sw.js文件返回后,請求最新資源流程

1. App緩存文件存放位置

image.png

2. 聲明攔截列表

private void initData() {
        // 主頁
        mMap.put(BaseUrl, "index.html");
        mMap.put(BaseUrl+"/", "index.html");
        mMap.put(BaseUrl + "/index.html", "index.html");
        // js文件
        mMap.put(BaseUrl + "/main-v1.js", "main.js");
        // css文件
        mMap.put(BaseUrl + "/static/css/main-v1.css", "static/css/main-v1.css");
        // 圖片
        mMap.put(BaseUrl + "/favicon.ico", "favicon.ico");
        mMap.put(BaseUrl + "/images/log.png", "images/log.png");
    }

3. App應(yīng)用攔截

            @TargetApi(VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view,
                    WebResourceRequest request) {
                String url = request.getUrl().toString();
                Log.d(TAG, "shouldInterceptRequest>5.0: url = " + url);
                if (!mIsLoadLocal) {

                    return super.shouldInterceptRequest(view, request);
                }
                if (mDataHelper.hasLocalResource(url)) {
                    Log.d(TAG, "shouldInterceptRequest>5.0: 資源命中:url="+url);
                    // super.shouldInterceptRequest(view, request); // 同時去請求網(wǎng)絡(luò)
                    WebResourceResponse response =
                            mDataHelper.getReplacedWebResourceResponse(getApplicationContext(),
                                    url);
                    if (response != null) {
                        return response;
                    }
                }
                return super.shouldInterceptRequest(view, request);
            }

第二次加載

第二次加載資源請求流程
ServiceWorker處理流程

之后的處理流程都是類似的

說明

1. 和http緩存結(jié)合使用

ServiceWorker中的請求,也會去http緩存中抓取的,所以可以結(jié)合使用,緩存策略:

  • index.html設(shè)定no-cache
  • 靜態(tài)資源文件緩存1年(通過每次打包后新版本號更新)
    以Nginx為例:
        location / {
            # root   html;
            root /Users/frru/nginxServers;
            index  index.html index.htm;
            autoindex on;               ##顯示索引  
            autoindex_exact_size on;    ##顯示大小
            autoindex_localtime on;     ##顯示時間
            if ($request_uri ~* ".html|htm") {
                add_header Cache-Control  "no-cache";
            }
            if ($request_uri ~* ".css|js|png") {
                expires 360d;
                # add_header Cache-Control "public, max-age=2592000, s-maxage=2592000";
                add_header wall  "hey!guys!give me a star.";
            }
        }

2. 每次項目打包時候,請同步更新sw.js文件,主要是更新其中cacheAll部分文件列表

例如版本變?yōu)関2之后,sw.js中cacheAll部分應(yīng)該為

//Service Worker安裝事件,其中可以預(yù)緩存資源
this.addEventListener('install', function(event) {
 console.log('install'); 
  //需要緩存的頁面資源
  var urlsToPrefetch = [
    './index.html',
    './main-v2.js',
    './static/css/main-v2.css',
    './static/image/log.png',
  ];

  event.waitUntil(
    caches.open(OFFLINE_CACHE_NAME).then(function(cache) {
      return cache.addAll(urlsToPrefetch);
    })
  );      
});

3. 注意:并未實踐

以上部分,關(guān)于sw.js部分的主動更新文件,我并未實現(xiàn)調(diào)試,理論上可行。之前我是采取邊返還ServiceWorker緩存,邊請求最新數(shù)據(jù)的方案,副作用上面已經(jīng)說明了。

4. sw.js主動更新文件和邊返回緩存,邊加載新資源,可以結(jié)合使用

5. 關(guān)于緩存文件列表

現(xiàn)在打包工具一般都會生成靜態(tài)資源列表,例如Create React App打包項目會生成一個asset-manifest.json文件:

{
  "main.css": "./static/css/main.484bfb43.chunk.css",
  "main.js": "./static/js/main.a763f74c.chunk.js",
  "main.js.map": "./static/js/main.a763f74c.chunk.js.map",
  "runtime~main.js": "./static/js/runtime~main.9eb600ee.js",
  "runtime~main.js.map": "./static/js/runtime~main.9eb600ee.js.map",
  "static/css/2.81f174d0.chunk.css": "./static/css/2.81f174d0.chunk.css",
  "static/js/2.3caf1636.chunk.js": "./static/js/2.3caf1636.chunk.js",
  "static/js/2.3caf1636.chunk.js.map": "./static/js/2.3caf1636.chunk.js.map",
  "index.html": "./index.html",
  "precache-manifest.95764df349dcc4f784ad6dbed9254278.js": "./precache-manifest.95764df349dcc4f784ad6dbed9254278.js",
  "service-worker.js": "./service-worker.js",
  "static/css/2.81f174d0.chunk.css.map": "./static/css/2.81f174d0.chunk.css.map",
  "static/css/main.484bfb43.chunk.css.map": "./static/css/main.484bfb43.chunk.css.map"
}

App應(yīng)用可以利用,拉取靜態(tài)文件。
也可以用來生成ServiceWorker主動獲取文件列表

騰訊VasSonic框架

簡介:騰訊SNG增值產(chǎn)品部技術(shù)團(tuán)隊研發(fā)的輕量級高性能Hybrid框架VasSonic
官方介紹文檔

業(yè)務(wù)場景

主要是應(yīng)對手Q的業(yè)務(wù),一些重點常用業(yè)務(wù),例如游戲分發(fā)中心、會員特權(quán)中心、個性裝扮商場等,是H5頁面,首屏加載時候,會比較慢。
隨著業(yè)務(wù)發(fā)展,有些業(yè)務(wù)形態(tài)發(fā)生了變化,例如個性推薦是動態(tài)內(nèi)容。

解決方案

VasSonic的前身

  1. 終端優(yōu)化
    一些常見的首頁優(yōu)化:懶加載、X5內(nèi)核預(yù)加載、WebView對象復(fù)用
  2. 靜態(tài)直出
  3. 離線預(yù)推(即熟悉的離線包)
    VasSonic做了功能增強,即離線增量包,大大減少了包大小。
  4. 動態(tài)直出功能
    因為業(yè)務(wù)形態(tài)發(fā)生了變化(個性推薦),做了對應(yīng)的功能。
    實時拉取用戶數(shù)據(jù)并在服務(wù)端渲染后返回給客戶端,也就是動態(tài)直出的方案。
  5. webso的嘗試
    QQ空間技術(shù)團(tuán)隊在解決動態(tài)直出方面的wns+html解決方案。不太適合手Q業(yè)務(wù)場景。有一定的借鑒參考。

綜上,不管改進(jìn)下,最后誕生了VasSonic。

VasSonic的誕生

  1. 并行加載
    就是把WebView初始化和請求資源由原來的串行改成并行,減少等待時間。
  2. 動態(tài)緩存
    把頁面拆分成靜態(tài)部分和動態(tài)部分,動態(tài)部分更新后去主動更新界面。
  3. 頁面分離
    對上面動態(tài)緩存的具體實現(xiàn)

后面的小章節(jié)都是具體使用VasSonic的規(guī)范。

???

VasSonic預(yù)加載

這兩種方式感覺都是針對手Q業(yè)務(wù)場景,在用戶打開手Q時候,預(yù)加載一些重要頁面和預(yù)判頁面。

騰訊瀏覽服務(wù) Service Worker最佳實踐

官文

ServiceWorker的不用講了,這里關(guān)于首屏的加載,比較狠,直接打成壓縮文件,讓x5內(nèi)核注冊成ServiceWorker,省去了用戶需要打開頁面才能注冊ServiceWorker的過程。

X5內(nèi)核Service Worker功能擴(kuò)展

首次訪問解決方案

首次訪問解決方案旨在用戶訪問業(yè)務(wù)前實現(xiàn)業(yè)務(wù)的資源緩存,讓用戶在第一次真正訪問業(yè)務(wù)時能夠讓業(yè)務(wù)頁面以最快的速度展示出來。針對該主旨,X5內(nèi)核實現(xiàn)了三套具體實現(xiàn)方案:

  1. 離線包方式
  2. X5內(nèi)核后臺云下發(fā)指令
  3. X5內(nèi)核擴(kuò)展接口
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容