【半原创】将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用于微信浏览器后,也节省了很多带宽。不过第一次加载还是有些慢的,毕竟还是有那么多数据。

posted @ 2014-11-15 15:28  refuly  阅读(5740)  评论(3编辑  收藏  举报