使用localstorage和預加載做到webview秒開

jopen 8年前發布 | 78K 次閱讀 Android開發 移動開發 JavaScript

提到網頁加載速度優化,大家都會想到靜態資源上CDN,CSS和JS文件合并,圖片合并成雪碧圖等常用手段;但是在某些特殊情況下這些常用方法也無法達到理想的效果。比如,在國際化場景下,很多國家還停留在2G網絡階段,無論如何優化,都無法避免過慢的網絡請求。最近一直在做國際化(主要是印尼和泰國)背景下的webview性能優化,也算有一些經驗。由于我們的產品是面向android用戶的,而android手機對H5支持很好,因此我們主要是應用H5的新特性。

1.H5的menifest緩存的局限

H5的menifest緩存機制是大家經常提起的方法,這是一種使用起來很簡單的機制,其基本原理就是:當menifest文件有更新時,就會更新整個應用,否則就不會請求網絡而是使用本地緩存的資源。但是我們使用起來卻有不少問題:比如即使menifest文件發生變化,應用也不能及時的更新。對于menifest機制我們的結論是:menifest可以用于緩存基本不發生變化的文件(比如基本樣式表base.css,javascript類庫jquery.js等), 對于業務代碼一定不要使用menifest緩存,否則可能導致業務代碼無法更新 。

新浪微博中大量的使用了menifest緩存機制,但是他們也只緩存了類庫和基本樣式表,而沒有緩存業務代碼。這是一個例子 http://card.weibo.com/article/h5/s#cid=1001603928694585472076 ,其menifest文件如下:

CACHE MANIFEST

v = 8f708b371de7949de35b8cec4dac678b

CACHE:

CSS and CSS resource image

http://u1.sinaimg.cn/apps/media/css/article_201511121505.css http://u1.sinaimg.cn/apps/media/img/bg_cricle.png http://u1.sinaimg.cn/apps/media/img/bg_criclein.png http://u1.sinaimg.cn/apps/media/img/bg_imgslayer.png http://u1.sinaimg.cn/apps/media/img/font/myfont_v3.eot http://u1.sinaimg.cn/apps/media/img/font/myfont_v3.woff http://u1.sinaimg.cn/apps/media/img/font/myfont_v3.ttf http://u1.sinaimg.cn/apps/media/img/font/myfont_v3.svg http://u1.sinaimg.cn/apps/media/img/lib/icons.svg http://u1.sinaimg.cn/apps/media/img/lib/icon_SmartisanNote.svg http://u1.sinaimg.cn/apps/media/img/lib/icon_Smart.svg

Placeholder image

http://ww3.sinaimg.cn/small/6f21f059jw1e4vxadm276j20f008ca9w.jpg

js file

http://u1.sinaimg.cn/apps/media/js/jquery.min.js http://u1.sinaimg.cn/apps/media/js/page/article/show/offline_201511121505.js

Emoticons top 10

http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/6a/laugh.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/0b/tootha_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/6d/lovea_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/c4/liwu_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/9d/sada_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/40/hearta_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/d9/ye_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/19/heia_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/8c/hsa_org.gif http://img.t.sinajs.cn/t4/appstyle/expression/ext/normal/d0/z2_org.gif

NETWORK: *</pre>

2.使用localstorage緩存js和css文件

localstorage設計之初是為了緩存應用數據的,關于是否應該使用它緩存js和css文件,知乎上有一篇討論: 靜態資源(JS/CSS)存儲在localStorage有什么缺點?為什么沒有被廣泛應用? 。其中得票最多的基本上概況了localstorage的適用場景以及優缺點:

PC上因為localstorage兼容性不好,而且網速較快,因此實用價值不大
移動端單頁面應用(webapp),因為localstorage兼容性好,網速慢,所以值得嘗試
localstorage的兼容性問題

UC的前端團隊推出了基于localstorage存儲前端靜態資源的 Scrat-webapp模塊化開發體系 ,而且基于這個模塊化體系開發了很多項目: 神馬視頻NBA直播 。大家可以通過chrome的開發者工具查看localstorage的使用情況。

基于以上的行業經驗,我們也選用了localstorage作為自己的前端資源緩存,并實現了自己的基于localstorage的靜態資源加載器,其實現原理可以參考我的博客: 在網頁中異步加載javascript 的XHR Eval方法。具體代碼如下:

;(function (global) {
    'use strict';

//檢查文件類型
var TYPE_RE = /\.(js|css)(?=[?&,]|$)/i;
function fileType(str) {
    var ext = 'js';
    str.replace(TYPE_RE, function (m, $1) {
        ext = $1;
    });
    if (ext !== 'js' && ext !== 'css') ext = 'unknown';
    return ext;
}

//將js片段插入dom結構
function evalGlobal(strScript){
    var scriptEl = document.createElement ("script");
    scriptEl.type= "text/javascript";
    scriptEl.text= strScript;
    document.getElementsByTagName("head")[0].appendChild(scriptEl) ;
}

//將css片段插入dom結構
function createCss(strCss) {
    var styleEl = document.createElement('style');
    document.head.appendChild(styleEl);
    styleEl.appendChild(document.createTextNode(strCss));
}

// 在全局作用域執行js或插入style node
function defineCode(url, str) {
    var type = fileType(url);
    if (type === "js"){
        //with(window)eval(str);
        evalGlobal(str);
    }else if(type === "css"){
        createCss(str);
    }
}

// 將數據寫入localstorage
var setLocalStorage = function(key, item){
    window.localStorage && window.localStorage.setItem(key, item);
}

// 從localstorage中讀取數據
var getLocalStorage = function(key){
    return window.localStorage && window.localStorage.getItem(key);
}

// 通過AJAX請求讀取js和css文件內容,使用隊列控制js的執行順序
var rawQ = [];
var monkeyLoader = {
    loadInjection: function(url,onload,bOrder){
        var iQ = rawQ.length;
        if(bOrder){
            var qScript = {key: null, response: null, onload: onload, done: false};
            rawQ[iQ] = qScript;
        }
        //有localstorage 緩存
        var ls = getLocalStorage(url);
        if(ls !== null){
            if(bOrder){
                rawQ[iQ].response = ls;
                rawQ[iQ].key = url;
                monkeyLoader.injectScripts();
            }else{
                defineCode(url, ls)
                if(onload){
                    onload();
                }
            }
        } else {
            var xhrObj = monkeyLoader.getXHROject();
            xhrObj.open('GET', url, true);
            xhrObj.send(null);
            xhrObj.onreadystatechange = function(){
                if(xhrObj.readyState == 4){
                    if(xhrObj.status == 200){
                        setLocalStorage(url, xhrObj.responseText);
                        if(bOrder){
                            rawQ[iQ].response = xhrObj.responseText;
                            rawQ[iQ].key = url;
                            monkeyLoader.injectScripts();
                        }else{
                            defineCode(url, xhrObj.responseText)
                            if(onload){
                                onload();
                            }
                        }
                    }
                }
            }
        }
    },

    injectScripts: function(){
        var len = rawQ.length;
        //按順序執行隊列中的腳本
        for (var i = 0; i < len; i++) {
            var qScript = rawQ[i];
            //沒有執行
            if(!qScript.done){
                //沒有加載完成
                if(!qScript.response){
                    console.error("raw code lost or not load!");
                    //停止,等待加載完成, 由于腳本是按順序添加到隊列的,因此這里保證了腳本的執行順序
                    break;
                }else{//已經加載完成了
                    defineCode(qScript.key, qScript.response)
                    if(qScript.onload){
                        qScript.onload();
                    }
                    delete qScript.response;
                    qScript.done = true;
                }
            }
        }
    },

    getXHROject: function(){
        //創建XMLHttpRequest對象
        var xmlhttp;
        if (window.XMLHttpRequest)
            {
                // code for IE7+, Firefox, Chrome, Opera, Safari
                xmlhttp=new XMLHttpRequest();
            } else {
                // code for IE6, IE5
                xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xmlhttp;
    }
};


global.monkeyLoader = monkeyLoader.loadInjection;

})(this);</pre>

在頁面中我們可以使用monkeyLoader加載自己的靜態資源

monkeyLoader('a.js',callbackA,true);
monkeyLoader('b.js',callbackB,true);

其中monkeyLoader的三個參數分別是:資源文件路徑、資源加載完成后的回調函數、以及資源是否需要有序。

3.基于fis3的localstorage集成解決方案

FIS3 作為一個優秀的前端工程化解決方案,其在業界的影響無處不在。作為狼長的一員,我們在項目中也大量使用FIS3,下面我們討論一下如何將localstorage集成到FIS3的編譯流程中。

我們的目的是將通過FIS3工程化處理后的文件列表傳遞到monkeyLoader,以實現基于localstorage的加載。FIS3提供了靜態資源映射表,當某個文件包含字符 RESOURCE_MAP ,就會用表結構數據替換此字符,而該表記錄了各個原始靜態資源經過工程化后的目標文件。基于此,我們只需要對monkeyLoader進行簡單的封裝即可。下面是具體的封裝代碼:

fisMonkeyLoader = function(resourceMap,resourceList){
        var pkgList = [];
        for(var idx in resourceList){
            var resourceItem = resourceList[idx];
            var pakItem = resourceMap['pkg'][resourceMap['res'][resourceItem]['pkg']]['uri'];
            pkgList[pakItem] =  1;
        }
        for(var pkgIdx in pkgList){
            monkeyLoader(pkgIdx,null,true);
        }

}</pre> <p>而使用方法也很簡單:</p>

__inline('../js/lib/monkeyLoader.js');
fisMonkeyLoader(__RESOURCE_MAP__,[
    'css/animate.css',
    'css/app.css',
    'js/lib/zepto-1.1.3.min.js',
    'js/app/app.js',
]);

fisMonkeyLoader根據 RESOURCE_MAP 找到原始資源對應的經過工程化處理以后的資源,并通過monkeyLoader進行加載。

也許你已經注意到了,使用localstorage只能加載同域的js和css文件,因為我們沒辦法通過js代碼讀取其他域下的靜態資源的內容。而且localstorage有大小限制,一般情況下每個域名下最多使用5M的本地存儲空間,因此我們務必要處理超出大小限制是的異常情況,各個瀏覽器拋出的異常不太一致, 這里給出了一個跨瀏覽器的異常檢測方案

4.通過預加載解決首次加載過慢問題

針對目前常見的hybrid加載,我們可以通過讓native客戶端預加載webview從而提升首次速度。我們的實現是:客戶端在啟動時打開一個1px大小(用戶不可見)的webview用于提前加載某些重要的頁面,而這些頁面通過上文中的localstorage機制存儲了幾乎全部js和css靜態資源,從而得到webview秒開的體驗。

</div>

來自: http://foio.github.io/localstorage-webview/

</code></code></code></code></code></code>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!