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

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

1.H5的menifest緩存的侷限

H5的menifest緩存機制是你們常常提起的方法,這是一種使用起來很簡單的機制,其基本原理就是:當menifest文件有更新時,就會更新整個應用,不然就不會請求網絡而是使用本地緩存的資源。可是咱們使用起來卻有很多問題:好比即便menifest文件發生變化,應用也不能及時的更新。對於menifest機制咱們的結論是:menifest能夠用於緩存基本不發生變化的文件(好比基本樣式表base.css,javascript類庫jquery.js等), 對於業務代碼必定不要使用menifest緩存,不然可能致使業務代碼沒法更新 。css

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

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: * 

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

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

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

UC的前端團隊推出了基於localstorage存儲前端靜態資源的 Scrat-webapp模塊化開發體系 ,並且基於這個模塊化體系開發了不少項目: 神馬視頻 、 NBA直播 。你們能夠經過chrome的開發者工具查看localstorage的使用狀況。java

基於以上的行業經驗,咱們也選用了localstorage做爲本身的前端資源緩存,並實現了本身的基於localstorage的靜態資源加載器,其實現原理能夠參考個人博客: 在網頁中異步加載javascript 的XHR Eval方法。具體代碼以下:node

;(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); 

在頁面中咱們可使用monkeyLoader加載本身的靜態資源jquery

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

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

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

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

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

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); } } 

而使用方法也很簡單:

__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秒開的體驗。

原文  http://foio.github.io/localstorage-webview/

相關文章
相關標籤/搜索