zoukankan      html  css  js  c++  java
  • 【半原创】将js和css文件装入localStorage加速程序执行

    首先感谢某某作者写的文章:http://www.jb51.net/article/12793.htm

    直接上代码,注意文件名为env.js

    原理如下:

        一次批量加要加载的文件存入数组,采用Ajax方式异步载入各个文件,然后采用循环方式逐个执行下载下来的Js或者Css文件,如果已经被缓存(localStorage)的则省略下载过程。

        由于JS采用的是单线程模式运行,在执行某一个js时会阻塞其它并发的js执行,所以会按顺序执行各个js。在执行完所有的脚本之后,图片会被浏览器接着加载,所以第一次加载速度略慢,后面就会比较快了。在JQuery Mobile 1.4.5+FireFox/微信浏览器下实测效果不错,IE就被省略了,我主要是要在微信浏览器下使用。

      1 //需要引用别的js的时候,就加上如Env.require("cookie.js"),或Env.require("/common/cookie.js"),是用相对路径还是绝对路径就看喜好了。
      2 //Env.require可用在页面模板中,也可用在js文件中,但一定要保证执行时env.js被显式引入。
      3 //多次Env.require同一个js(不管用相对还是绝对),只有第一次会加载,所以不会重复。
      4 
      5 //程序最后发行的版本,用于作为缓存键的前缀,快速更新缓存
      6 var envLastVer = '2014_11_17_17_03';
      7 
      8 //用于存放通道名称及通信对象的类,这样可以通过不同通道名称来区分不同的通信对象  
      9 function HttpRequestObject() {
     10     this.chunnel = null;
     11     this.instance = null;
     12 }
     13 
     14 //用于获取的脚本或css文件保存对象
     15 function HttpGetObject() {
     16     this.url = null;        //要下载的文件路径
     17     this.cache_key = null;  //缓存键
     18     this.chunnel = null;    //通道名
     19     this.type = null;       //类型,js或css
     20     this.is_fill = false;   //内容是否被填充
     21     this.is_exec = false;   //内容是否已被执行,防止分几大块载入后重复执行
     22 }
     23 
     24 //通信处理类,可以静态引用其中的方法  
     25 var Request = new function () {
     26 
     27     //通信类的缓存  
     28     this.httpRequestCache = new Array();
     29 
     30     //创建新的通信对象 
     31     this.createInstance = function () {
     32         var instance = null;
     33         if (window.XMLHttpRequest) {
     34             //mozilla  
     35             instance = new XMLHttpRequest();
     36             //有些版本的Mozilla浏览器处理服务器返回的未包含XML mime-type头部信息的内容时会出错。
     37             //因此,要确保返回的内容包含text/xml信息  
     38             if (instance.overrideMimeType) {
     39                 instance.overrideMimeType = "text/xml";
     40             }
     41         }
     42         else if (window.ActiveXObject) {
     43             //IE  
     44             var MSXML = ['MSXML2.XMLHTTP.5.0', 'Microsoft.XMLHTTP', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
     45             for (var i = 0; i < MSXML.length; i++) {
     46                 try {
     47                     instance = new ActiveXObject(MSXML[i]);
     48                     break;
     49                 }
     50                 catch (e) {
     51                 }
     52             }
     53         }
     54         return instance;
     55     }
     56 
     57     /**  
     58     * 获取一个通信对象  
     59     * 若没指定通道名称,则默认通道名为"default"  
     60     * 若缓存中不存在需要的通信类,则创建一个,同时放入通信类缓存中  
     61     * @param _chunnel:通道名称,若不存在此参数,则默认为"default"  
     62     * @return 一个通信对象,其存放于通信类缓存中  
     63     */
     64     this.getInstance = function (_chunnel) {
     65         var instance = null;
     66         var object = null;
     67         if (_chunnel == undefined)//没指定通道名称  
     68         {
     69             _chunnel = "default";
     70         }
     71         var getOne = false;
     72         for (var i = 0; i < this.httpRequestCache; i++) {
     73             object = HttpRequestObject(this.httpRequestCache[i]);
     74             if (object.chunnel == _chunnel) {
     75                 if (object.instance.readyState == 0 || object.instance.readyState == 4) {
     76                     instance = object.instance;
     77                 }
     78                 getOne = true;
     79                 break;
     80             }
     81         }
     82         if (!getOne) //对象不在缓存中,则创建  
     83         {
     84             object = new HttpRequestObject();
     85             object.chunnel = _chunnel;
     86             object.instance = this.createInstance();
     87             this.httpRequestCache.push(object);
     88             instance = object.instance;
     89         }
     90         return instance;
     91     }
     92 
     93     /**  
     94     * 客户端向服务端发送请求  
     95     * @param _url:请求目的  
     96     * @param _data:要发送的数据  
     97     * @param _processRequest:用于处理返回结果的函数,其定义可以在别的地方,需要有一个参数,即要处理的通信对象  
     98     * @param _chunnel:通道名称,默认为"default"  
     99     * @param _asynchronous:是否异步处理,默认为true,即异步处理
    100     * @param _paraObj:相关的参数对象 
    101     */
    102     this.send = function (_url, _data, _processRequest, _chunnel, _asynchronous, _paraObj) {
    103         if (_url.length == 0 || _url.indexOf("?") == 0) {
    104             alert("由于目的为空,请求失败,请检查!");
    105             return;
    106         }
    107         if (_chunnel == undefined || _chunnel == "") {
    108             _chunnel = "default";
    109         }
    110         if (_asynchronous == undefined) {
    111             _asynchronous = true;
    112         }
    113         var instance = this.getInstance(_chunnel);
    114         if (instance == null) {
    115             alert("浏览器不支持ajax,请检查!")
    116             return;
    117         }
    118         if (_asynchronous == true && typeof (_processRequest) == "function") {
    119             instance.onreadystatechange = function () {
    120                 if (instance.readyState == 4) // 判断对象状态  
    121                 {
    122                     if (instance.status == 200) // 信息已经成功返回,开始处理信息  
    123                     {
    124                         _processRequest(instance, _paraObj);
    125                     }
    126                     else {
    127                         alert("您所请求的页面有异常,请检查!");
    128                     }
    129                 }
    130             }
    131         }
    132         //_url加一个时刻改变的参数,防止由于被浏览器缓存后同样的请求不向服务器发送请求  
    133         if (_url.indexOf("?") != -1) {
    134             _url += "&requestTime=" + (new Date()).getTime();
    135         }
    136         else {
    137             _url += "?requestTime=" + (new Date()).getTime();
    138         }
    139         if (_data.length == 0) {
    140             instance.open("GET", _url, _asynchronous);
    141             instance.send(null);
    142         }
    143         else {
    144             instance.open("POST", _url, _asynchronous);
    145             instance.setRequestHeader("Content-Length", _data.length);
    146             instance.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    147             instance.send(_data);
    148         }
    149         if (_asynchronous == false && typeof (_processRequest) == "function") {
    150             _processRequest(instance, _paraObj);
    151         }
    152     }
    153 }
    154 
    155 var Env = new function () {
    156     this.needLoadObject = new Array();
    157 
    158     //获取env.js文件所在路径
    159     this.envPath = null;
    160     this.getPath = function () {
    161         this.envPath = document.location.pathname;
    162         this.envPath = this.envPath.substring(0, this.envPath.lastIndexOf("/") + 1);
    163         var _scripts = document.getElementsByTagName("script");
    164         var _envPath = null;
    165         var _scriptSrc = null;
    166         for (var i = 0; i < _scripts.length; i++) {
    167             _scriptSrc = _scripts[i].getAttribute("src");
    168             if (_scriptSrc && _scriptSrc.indexOf("env.js") != -1) {
    169                 break;
    170             }
    171         }
    172         if (_scriptSrc != null) {
    173             if (_scriptSrc.charAt(0) == '/') {
    174                 this.envPath = _scriptSrc.substr(0, _scriptSrc.length - 6);
    175             }
    176             else {
    177                 this.envPath = this.envPath + _scriptSrc.substr(0, _scriptSrc.length - 6);
    178             }
    179         }
    180     }
    181     this.getPath();
    182 
    183     //获取文件后缀名
    184     this.getFileExt = function (fileUrl) {
    185         var d = /.[^.]+$/.exec(fileUrl);
    186         return d.toString().toLowerCase();
    187     }
    188 
    189     //依次放入要载入的文件
    190     this.pushNeedLoad = function (url) {
    191         var _absUrl = null;
    192         if (url.charAt(0) == '/')
    193             _absUrl = url;
    194         else
    195             _absUrl = this.envPath + url;
    196 
    197         var object = new HttpGetObject();
    198         object.url = _absUrl;
    199         object.cache_key = envLastVer + _absUrl;    //利用版本号+绝对路径生成缓存键
    200         object.chunnel = 'ch' + (this.needLoadObject.length + 1);
    201         object.type = this.getFileExt(_absUrl);
    202 
    203         //尝试从缓存获取
    204         var cacheContent = localStorage.getItem(object.cache_key);
    205         if (cacheContent) { object.is_fill = true; }
    206 
    207         this.needLoadObject.push(object);
    208         return this;
    209     }
    210 
    211     //依次装载要处理的文件
    212     this.batchLoad = function () {
    213         for (var i = 0; i < this.needLoadObject.length; i++) {
    214             var item = this.needLoadObject[i];
    215             var processGet = function (_instance, _paraObj) {
    216                 localStorage.setItem(_paraObj.cache_key, _instance.responseText);    //缓存文件
    217                 _paraObj.is_fill = true;
    218             }
    219             if (item.is_fill == false) {
    220                 Request.send(item.url, "", processGet, item.chunnel, false, item);  //采用同步方式载入
    221             }
    222         }
    223         return this;
    224     }
    225 
    226     //依次执行要处理的文件
    227     this.batchExec = function () {
    228         var runCss = function (_css) { document.write('<style type="text/css">' + _css + '</style>'); }
    229         var runJs = function (_js) {
    230             if (window.execScript)
    231                 window.execScript(_js);
    232             else
    233                 window.eval(_js);
    234         }
    235         //依次执行,由于js为单线程执行,每执行一个js都会阻塞其它,所以可以保证顺序执行
    236         for (var i = 0; i < this.needLoadObject.length; i++) {
    237             var item = this.needLoadObject[i];
    238             if (item.is_exec == false) {
    239                 if (item.type == '.js') {
    240                     runJs(localStorage.getItem(item.cache_key));
    241                     item.is_exec = true;  //标记已执行,下次不会再执行
    242                 }
    243                 else if (item.type == '.css') {
    244                     runCss(localStorage.getItem(item.cache_key));
    245                     item.is_exec = true;  //标记已执行,下次不会再执行
    246                 }
    247             }
    248         }
    249     }
    250 }

    下面是调用方法: 

     1         Env.pushNeedLoad("jquery.mobile-1.4.5/jquery.min.js")
     2             .pushNeedLoad("jquery.mobile-1.4.5/jquery.mobile-1.4.5.min.css")
     3             .pushNeedLoad("/plus_in/weixin/procedure/scripts/task.util.js")
     4             .pushNeedLoad("/plus_in/weixin/procedure/scripts/emcp.mobile.js")
     5             .pushNeedLoad("/plus_in/weixin/procedure/scripts/common.index.js")
     6             .pushNeedLoad("jquery.mobile-1.4.5/jquery.mobile-1.4.5.min.js")
     7             .pushNeedLoad("mobiscroll.2.6/css/mobiscroll.custom-2.6.2.min.css")
     8             .pushNeedLoad("mobiscroll.2.6/js/mobiscroll.custom-2.6.2.min.js")
     9             .pushNeedLoad("/plus_in/weixin/procedure/style/base.css")
    10             .batchLoad().batchExec();

    通过火狐F12观察,发现上面的脚本第一次会被加载,后面将会直接从localstorage中读取,节省了很多,将js用于微信浏览器后,也节省了很多带宽。不过第一次加载还是有些慢的,毕竟还是有那么多数据。

  • 相关阅读:
    HTML基础 整理
    今天课堂总结
    课后习题
    JAVA的文件创建
    JAVA_输入输出流 异常处理
    12.23流水账号
    12.22 repeater 删除
    12.22 repeater 修改
    12.22 repeater 添加
    12.22 repeater 主页
  • 原文地址:https://www.cnblogs.com/wubin264/p/load_js_css_into_localstorage.html
Copyright © 2011-2022 走看看