JavaScript预加载图片与浏览器文件缓存

目前的主流浏览器都支持客户端缓存,用户在浏览网页的时候,浏览器会把网页里的文件数据缓存起来,当用户再次打开访问过的页面时,浏览器可以从本地缓存读取文件,减少网络下载文件等待的时间,同时也减轻了服务器的压力。总之浏览器文件缓存是个好东西,对用户好,对服务器好,对传输网络好;你好,我也好,大家好那是真的好。

有了浏览器的这个缓存,给开发者带来更多的发挥空间,不论是做后端程序开发,还是前端工程师,都离不开这块好地方。缓存管理的好坏,直接会影响到网站的用户体验。缓存做的好,占用的网络带宽小,服务器压力小,用户也感觉网站速度快,心情十分愉悦。缓存没控制好,每次都让用户多加载一些重复的文件,网页加载的时间变长,用户流失是必然的。

对于一些复杂的网页交互程序,比如网页游戏,往往需要的内容会比较多。如果优化的不好,自然要加载很多东西。如果一点一点的加载,用户操作几步就要等待一次网络加载,体验很不好。现在大部分这种复杂的应用,都会先让用户进入一段Loading动画,等需要的内容加载完毕,后面用户就可以很流畅的进行操作了。

目前使用Flash可以很容易的实现内容的预加载(Loading),对于非Flash的互联网产品,可以直接使用JavaScript来实现预加载。从目前网页交互应用程序来看,最占带宽的无非就是一些较大的图片文件、样式表文件和脚本文件,实现这些文件的预加载可以使用Iframe或者Image对象等来解决。

于是简单写了一个测试的小例子(查看效果),JavaScript代码:

JavaScript
var preloadFiles={
    //debug模式
    debug:false,
 
  //图片对象
  imgLoader:null,
  //进度条
  processBar:null,
 
  //初始化变量
  initVars:function(){
      //加载失败次数
      this.errorCount=0;
      //待加载文件列表
      this.filesList=[];
      //待加载的文件总数
      this.filesNumber=0;
      //当前正在加载的文件索引
      this.currentFileIndex=0;
      //已经成功加载的文件
      this.filesDownloadSuccess=[];
      //预先加载结束
      this.preloadFinished=false;
  },
 
  //进度条
  showProcessBar:function(){
      if(this.processBar===null){
        var barContainer=document.createElement('div');
      barContainer.style.cssText='position:absolute; left:2px; top:2px; width:200px; height:6px; border:#9d9d9d 1px solid; padding:1px;';
        this.processBar=document.createElement('div');
      this.processBar.style.cssText='width:0%; height:6px; background-color:#33df33; font-size:6px;';
 
      barContainer.appendChild(this.processBar);
      document.body.appendChild(barContainer);
    }else{
        this.processBar.style.width=Math.ceil(((this.currentFileIndex-1)/this.filesNumber)*100).toString()+'%';
    }
  },
 
  //加载成功
  loadSuccess:function(){
      if(preloadFiles.debug){console.log('√ file "'+preloadFiles.filesList[preloadFiles.currentFileIndex-1]+'" load success!');}
    preloadFiles.filesDownloadSuccess.push(preloadFiles.filesList[preloadFiles.currentFileIndex-1]);
    preloadFiles.downloadElement(preloadFiles.filesList[preloadFiles.currentFileIndex++]);
  },
 
  //加载失败
  loadError:function(){
      preloadFiles.errorCount++;
    if(preloadFiles.debug){console.error('× file "'+preloadFiles.filesList[preloadFiles.currentFileIndex-1]+'" load error!');}
    preloadFiles.downloadElement(preloadFiles.filesList[preloadFiles.currentFileIndex++]);
  },
 
  //添加任务队列
  addFileTasks:function(filesArray){
      //把新的文件列表添加到任务队列
      this.filesList=this.filesList.concat(filesArray);
    this.filesNumber=this.filesList.length;
    this.downloadElement(this.filesList[this.currentFileIndex++]);
 
    if(this.debug){
        console.log('>>add '+filesArray.length+' files to task!');
        console.log(this.filesList);
    }
  },
 
  //load CSS
  loadCss:function(url){
    var cssLoader=document.createElement('link');
    cssLoader.setAttribute('href',url);
    cssLoader.setAttribute('type','text/css');
    cssLoader.setAttribute('rel','stylesheet');
    document.head.appendChild(cssLoader);
  },
 
  //load Script
  loadScript:function(url){
      var scriptLoader=document.createElement('script');
      scriptLoader.setAttribute('src',url);
      scriptLoader.setAttribute('type','text/javascript');
    scriptLoader.onload=this.loadSuccess;
    scriptLoader.onerror=this.loadError;
      document.body.appendChild(scriptLoader);
  },
 
  //load Image
  loadImage:function(url){
      if(this.imgLoader===null){
        this.imgLoader=document.createElement('img');
      this.imgLoader.setAttribute('src',url);
      this.imgLoader.setAttribute('width','0');
      this.imgLoader.setAttribute('height','0');
      document.body.appendChild(this.imgLoader);
      this.imgLoader.onload=this.loadSuccess;
      this.imgLoader.onerror=this.loadError;
    }else{
        this.imgLoader.setAttribute('src',url);
    }
  },
 
  //创建dom元素
  downloadElement:function(url){
      this.showProcessBar();
 
      if(this.currentFileIndex>this.filesNumber){
        setTimeout(function(){preloadFiles.processBar.parentNode.style.cssText='display:none;';},2000);
      this.preloadFinished=true;
      return false;
    }
 
    //匹配eg: xxx.css?version=n
      if(/\.(jpg|png|gif|bmp|jpeg|js|css)(\\?.+)?$/.test(url)){
        if(this.debug){
          //console.info('subfix=.'+RegExp.$1);
        console.log(' ');
          console.info('>>current is loading '+url+', ['+(this.currentFileIndex)+'/'+this.filesNumber+']');
      }
      switch(RegExp.$1){
          case 'css':
            this.loadCss(url);
          this.downloadElement(this.filesList[this.currentFileIndex++]);
        break;
        case 'js':
            this.loadScript(url);
        break;
        default:
            this.loadImage(url);
        break;
      }
    }else{
        if(this.debug){
          console.log(' ');
          console.warn(url+' is not a valid file, ['+(this.currentFileIndex)+'/'+this.filesNumber+']');
      }
      this.downloadElement(this.filesList[this.currentFileIndex++]);
    }
  },
 
    //初始化
  initUrlArray:function(filesArray,urlPrefix){
      for(var i=0,len=filesArray.length;i<len;i++){
        if(!/^http:|https:/.test(filesArray[i])){
          filesArray[i]=urlPrefix+filesArray[i];
      }
    }
    return filesArray;
  },
 
  //加载文件
    load:function(filesArray,urlPrefix,debug){
      if(filesArray&&filesArray.length&&filesArray.length>0){
        if(filesArray.constructor!==Array){alert("参数错误,第一个参数需要为数组"); return false;}
    }else{
        return;
    }
    if(urlPrefix){
        if(urlPrefix===true){debug=urlPrefix; urlPrefix='';}
      if(urlPrefix.constructor!==String){alert("参数错误,第二个参数需要为字符串"); return false;}
    }
 
      this.debug=debug||false;
    //如果加载还未开始,或者已经加载结束,则重新初始化变量
    if(!this.filesNumber||this.preloadFinished){this.initVars();}
 
      if(urlPrefix){
        filesArray=this.initUrlArray(filesArray,urlPrefix);
    }
 
    //添加任务队列
    this.addFileTasks(filesArray);
  }
}

HTML代码部分:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>image_load_test</title>
</head>

<body>
<ul>
  <li><input type="button" value="显示已缓存图片" /></li>
</ul>

<div id="test" class="display_none"></div>
<script type="text/javascript" src="index_v2.js"></script>
<script type="text/javascript">
//preloadFiles.load(ArrayFiles,UrlPrefix,debug);
preloadFiles.load([
    '1.txt',
  '2.jpg',
  'http://www.baid.com/a.jpg',
  'http://www.baidu.com/img/baidu_sylogo1.gif',
  'http://localhost/demo_test/image_load_test/image_load_tes.js',
  'http://www.google.com.hk/intl/zh-CN/images/logo_cn.png',
  'http://www.baidu.com/img/baidu_sylogo1.gif'
],true);

preloadFiles.load(['3.txt','4.jpg'],'http://www.baidu.com/',true);

//display downloaded images
(function displayImages(){
    var input=document.getElementsByTagName("input")[0];
  input.onclick=function(){
      if(!preloadFiles.preloadFinished){alert('图片加载中,请稍等!');return false;}
      var url='';
    var imagesList='';
      for(var i=0,len=preloadFiles.filesDownloadSuccess.length;i<len;i++){
        url=preloadFiles.filesDownloadSuccess[i];
      if(/\.(jpg|gif|png|jpeg|bmp)$/.test(url)){
          imagesList+='<li><img src="'+url+'" /></li>';
      }
    }
    document.getElementsByTagName('ul')[0].innerHTML=imagesList;

    //加载样式
    preloadFiles.load(['image_load_test.css'],true);
  }
})();
</script>
</body>
</html>

查看运行效果,如果使用Firefox/Chrome/IE8+,可以使用控制台查看加载状态。图片预加载完毕之后,断开网络,点击“显示已缓存图片”按钮,缓存图片会立即显示,而不需要请求网络。

画了一张浏览器文件缓存的分析图:

如果有兴趣,你还可以阅读以下文章:

http://www.fantxi.com/blog/archives/preload-images-css-js/

http://nootn.com/blog/Develop/44/

回复 (0)

› 尚无评论。

发表评论

允许使用的标签 - 您可以在评论中使用如下的 HTML 标签以及属性。

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">