目的:
利用localstorage進行關(guān)鍵Js和CSS緩存,節(jié)約下載。
知識點:
1.瀏覽器默認緩存機制
2.Localstorage
3.異步改造為同步
講解:
1.瀏覽器默認會對JS進行緩存。使得下一次進入的時候變?yōu)?04請求。然而,用戶重新刷新等操作時,瀏覽器會清除這些緩存撵术。所以這些緩存是不可控的。
2.Localstorage的大小大約在5M话瞧。
3.異步下載要想保證順序嫩与,可以采用遞歸的方式寝姿。
設(shè)計框架:
代碼實現(xiàn):
window.Xhrfactory = function() {
this.init();
}
window.Xhrfactory.prototype = {
init: function() {
this.xhr = this.create();
},
create: function() {
var xhr = null;
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject('Msml2.Xmlhttp'); //IE7及以后版本IE
}
else {
xhr = new ActiveXobject('Microsoft.Xmlhttp');//其他版本IE
}
return xhr;
},
readystate: function(callback) {
this.xhr.onreadystatechange = function(){
if(this.readyState === 4 && this.status === 200) { //this發(fā)生了默認綁定,指向了xhr
callback(this.responseText);
console.log(this);
}
}
},
para: function(data) {
var datastr = '';
if(data && Object.prototype.toString.call(data) === "[object object]") { //判斷對象是否為對象
for(var i in data) {
for (var i=0;i<length;i++){
datastr += i + '='
data[i] + '&';
}
}
datastr = '?' + datastr;
}
return datastr;
},
get: function(url,data,callback) {
this.readystate(callback);
var newurl = url;
var datastr = this.para(data);
newurl = url + datastr;
this.xhr.open('get',newurl,false);//這里不能使用true 需要使用false來解決異步問題
this.xhr.send();
}
};
var localStorageSign = 'on'; //后臺開關(guān)控制划滋,防止緩存失效
var resourceVersion = '20171112'; //版本控制
//本地SDK方法
window.mLocalSdk = {//注意相互依賴饵筑,需要按順序加載
resourceJavascriptList: [
{
id:'0',
url:'./src/js/jquery.js',
type:'javascript'
},
{
id:'1',
url:'./src/js/bootstrap.js',
type:'javascript'
},
{
id:'2',
url:'./src/js/test.js',
type:'javascript'
}
],
resourceCssList : [],
noNeedUpdate: (function() {
return localStorage.getItem('resourceVersion') === resourceVersion;
})(),
isIE: (function(){
if (!!window.ActiveXObject || "ActiveXObject" in window) //通過能力判斷IE
return true;
else
return false;
})(),
//判斷是否超過localstorage的閥值
checkHedge: function(){
var localStorageLength = localStorage.length;
var localStorageSize = 0;
for (var i = 0; i < localStorageLength; i++) {
var key = localStorage.key(i);
localStorageByte += localStorage.getItem(key).length;
}
return localStorageSize;
},
saveSDK: function() {
try {
localStorage.setItem("resourceVersion",resourceVersion);
} catch (Excepition) {
if (Exception.name == "QuotaExceededError") {
localStorage.clear();
localStorage.setItem("resourceVersion",resourceVersion);
}
alert('QuotaExceededError');
}
for(var i = 0; i< this.resourceJavascriptList.length; i++) {
var _self = this;//保存this指針
(function(i){
var scriptId = _self.resourceJavascriptList[i].id;
var xhr = new Xhrfactory();
xhr.get(_self.resourceJavascriptList[i].url,null,function(data){
try {
localStorage.setItem(scriptId,data);
} catch (Exception) {
console.log('Excpetion',Excpetion);
if (Exception.name == "QuotaExceededError") {
localStorage.clear();
localStorage.setItem("resourceVersion",resourceVersion);
}
}
})
})(i);
}
for(var i = 0; i< this.resourceCssList.length; i++) {
var _self = this;//保存this指針
(function(i){
var cssId = _self.resourceCssList[i].id;
var xhr = new Xhrfactory();
xhr.get(_self.resourceCssList[i].url,null,function(data){
try {
localStorage.setItem(cssId,data);
} catch (Exception) {
console.log('Excpetion',Excpetion);
if (Exception.name == "QuotaExceededError") {
localStorage.clear();
localStorage.setItem("resourceVersion",resourceVersion);
}
}
})
})(i);
}
},
startup: function(){
var _self = this;
if (localStorageSign === 'on' && !this.isIE && window.localStorage) {
if (this.noNeedUpdate === true) {//使用本地 則在本地進行內(nèi)聯(lián)引入
return (function() {
for (var i = 0; i < _self.resourceJavascriptList.length; i++) {
var scriptId = _self.resourceJavascriptList[i].id;
window.mDomUtils.addJavascriptByInline(scriptId);
}
for (var i = 0; i < _self.resourceCssList.length; i++) {
var cssId = _self.resourceCssList[i].id;
var cssString = localStorage.getItem(cssId);
window.mDomUtils.addCssByInline(cssString);
}
})();
}
else {
return (function() {
_self.saveSDK(); //這里會存在異步回調(diào)問題 需要確保保存后再進行后面的操作 通過open.false解決
for (var i = 0; i < _self.resourceJavascriptList.length; i++) {
var scriptId = _self.resourceJavascriptList[i].id;
window.mDomUtils.addJavascriptByInline(scriptId);
}
for (var i = 0; i < _self.resourceCssList.length; i++) {
var cssId = _self.resourceCssList[i].id;
var cssString = localStorage.getItem(cssId);
window.mDomUtils.addCssByInline(cssString);
}
})();
}
}
else {
return (function() {//不使用本地,則在外鏈中引入進行下載处坪,這里存在兩個異步問題:1.JS沒下載完就繼續(xù)執(zhí)行其他程序 2.jq和bs無法保證先后依賴順序下載
for (var i = 0; i < _self.resourceCssList.length; i++) {
window.mDomUtils.addCssByLink(_self.resourceCssList[i]['url']);
}
window.mDomUtils.addJavascriptByLink(_self.resourceJavascriptList,0);
})()
}
}
}
window.mDomUtils = {
//內(nèi)聯(lián)方式 直接寫代碼
addJavascriptByInline: function(scriptId) {
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
script.id = scriptId;
var heads = document.getElementsByTagName('head');
if(heads.length) {
heads[0].appendChild(script);
}
else {
document.documentElement.appendChild(script);
}
script.innerHTML = localStorage.getItem(scriptId);
},
//外鏈方式 直接引用 需要同步加載js
addJavascriptByLink: function(list,count) {
/* var script = document.createElement('script');
script.setAttribute('type','text/javascript');
script.setAttribute('src',url);
script.id = scriptId;
var heads = document.getElementsByTagName('head');
if (heads.length) {
heads[0].appendChild(script);
} else{
document.documentElement.appendChild(script);
}///這種方式會引起js異步加載根资,無法達到同步效果 不可取
/ var xhr = new Xhrfactory();
xhr.get(url,null,function(data){
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
script.setAttribute('src',url);
script.id = scriptId;
var heads = document.getElementsByTagName('head');
if (heads.length) {
heads[0].appendChild(script);
} else{
document.documentElement.appendChild(script);
}
});//這種方式會引起js加載兩次 不可取*/
var head= document.getElementsByTagName('head');
var script= document.createElement('script');
script.type= 'text/javascript';
script.src = list[count].url;
if (head.length) {
head[0].appendChild(script);
} else{
document.documentElement.appendChild(script);
}//最終選擇這種方式進行遞歸調(diào)用 借用jquery思想
script.onload = script.onreadystatechange = function() {
if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete" ) {
count++;
if (count < list.length){
window.mDomUtils.addJavascriptByLink(list,count);
}
else {
return true;
}
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
}
};
},
addCssByInline: function(cssString) {
var link = document.createElement('link');
link.setAttribute('type','text/css');
link.setAttribute('rel','stylesheet');
if(link.stylesheet){
link.stylesheet.cssText = cssString;
}
else {
var cssText = document.createTextNode(cssString);
link.appendChild(cssText);
}
var heads = document.getElementsByTagName('head');
if(heads.length) {
heads[0].appendChild(link);
}
else {
document.documentElement.appendChild(link);
}
},
addCssByLink: function(url) {
var link = document.createElement('link');
link.setAttribute('href',url);
link.setAttribute('type','text/css');
link.setAttribute('rel','stylesheet');
var heads = document.getElementsByTagName('head');
if(heads.length) {
heads[0].appendChild(link);
}
else {
document.documentElement.appendChild(link);
}
}
}
引入方式:
代碼地址:
git clone https://github.com/kingykking/cacheSDK.git
使用效果:
使用前:
使用后:
錯誤記錄:
1.沒有按照順序引入。
2.外鏈沒有按照順序引入