1-7 basket.js localstorage.js缓存cssjs

Posted wujiaolong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1-7 basket.js localstorage.js缓存cssjs相关的知识,希望对你有一定的参考价值。

basket.js 源码分析

 
api 使用文档:
 
 
 

一、前言

basket.js 可以用来加载js脚本并且保存到 LocalStorage 上,使我们可以更加精准地控制缓存,即使是在 http 缓存过期之后也可以使用。因此可以使我们防止不必要的重新请求 js 脚本,提升网站加载速度。

可以到 basket.js 的 Github 上查看更多的相关信息。

由于之前在工作中使用过 basket.js ,好奇它的实现原理,因此就有了这篇分析 basket.js 源码的文章。

二、简单的使用说明

basket.js 的使用非常简单,只要引入相应的js脚本,然后使用 basket 的 require 方法加载就可以了,例子:

<!DOCTYPE html><html><head><title>basket.js demo</title><script src="basket.full.min.js"></script></head><body><script>basket.require({url:\'helloworld.js\'});</script></body></html>

第一次加载,由于helloworld.js 只有一行代码alert(\'hello world\');, 所以运行该demo时就会弹出 "hello world"。并且对应的 js 会被保存到 LocalStorage:

此时对应的资源加载情况:

刷新一次页面,再查看一次资源的加载情况:

可以看到已经没有再发送 helloworld.js 相关的请求,因为 LocalStorage 上已经有对应的缓存了,直接从本地获取即可。

三、实现流程

流程图

细节说明

处理参数

参数处理就是根据已有的参数初始化未指定的参数。例如 require 方法支持 once 参数用来表示是否只执行一次对应 JS,execute 参数标示是否加载完该 JS 之后立刻执行。所以参数处理这一步骤就会根据是否执行过该 JS 和 once 参数是否为 ture 来设置execute参数。

获取缓存

调用 localStorage.getItem方法获取缓存。存入的 key 值为 basket- 加上 JS 文件的 URL。以上面加载 helloworld.js 为例,key 值为:basket-helloworld.js获取的缓存为一个缓存对象,里面包含 JS 代码和相关的一些信息,例如:

  1. {
  2. "url":"helloworld.js?basket-unique=123",
  3. "unique":"123",
  4. "execute":true,
  5. "key":"helloworld.js",
  6. "data":"alert(\'hello world\');",
  7. "originalType":"application/javascript",
  8. "type":"application/javascript",
  9. "skipCache":false,
  10. "stamp":1459606005108,
  11. "expire":1477606005108
  12. }
 

其中 data 属性对应的值就是 JS 代码。

判断缓存是否有效

判断比较简单,根据缓存对象里面的版本号 unique 和过期时间 expire 等来判断。这和浏览器使用 Expire 和 Etag 头部来判断 HTTP 缓存是否有效相似。最大的不同就是缓存完全由 JS 控制!这也就是 basket.js 最大的作用。让我们更好的控制缓存。默认的过期时间为5000小时,也就是208.33天。

判断代码:

/** * 判断ls上的缓存对象是否过期 * @param{object} source 从ls里取出的缓存对象 * @param{object} obj 传入的参数对象 * @returns {Boolean} 过期返回true,否则返回false */var isCacheValid =function(source, obj) {return!source ||// 没有缓存数据返回truesource.expire-+newDate() <0||// 超过过期时间返回trueobj.unique!==source.unique||// 版本号不同的返回true (basket.isValidItem&&!basket.isValidItem(source, obj));// 自定义验证函数不成功的返回true};

Ajax获取JS

普通的利用 XMLHttpRequest 请求。

缓存到LocalStorage

调用localStorage.setItem方法保存缓存对象。一般来说,只要这一行代码就能完成本步骤。但是LocalStorage保存的数据是有大小限制的!我利用 chrome 做了一个小测试,保存500KB左右的东西就会令��� Resources 面板变卡,2M 几乎可以令到 Resources 基本卡死,到了 5M 就会超出限制,浏览器抛出异常:

 

OMException: Failed to execute\'setItem\'on\'Storage\': Setting the valueof\'basket-http://file.com/ykq/wap/v3Templates/timeout/timeout/large.js\' exceeded the quota

 

因此需要使用 try catch 对localStorage.setItem方法进行异常捕获。当没容量不足时就需要根据保存时间逐一删除 LocalStorage 的缓存对象。

相关代码:

 

  1. /**
  2. * 把缓存对象保存到localStorage中
  3. * @param {string} key ls的key值
  4. * @param {object} storeObj ls的value值,缓存对象,记录着对应script的对象、有url、execute、key、data等属性
  5. * @returns {boolean} 成功返回true
  6. */
  7. var addLocalStorage =function( key, storeObj ){
  8. // localStorage对大小是有限制的,所以要进行try catch
  9. // 500KB左右的东西保存起来就会令到Resources变卡
  10. // 2M左右就可以令到Resources卡死,操作不了
  11. // 5M就到了Chrome的极限
  12. // 超过之后会抛出如下异常:
  13. // DOMException: Failed to execute \'setItem\' on \'Storage\': Setting the value of \'basket-http://file.com/ykq/wap/v3Templates/timeout/timeout/large.js\' exceeded the quota
  14. try{
  15. localStorage.setItem( storagePrefix + key, JSON.stringify( storeObj ));
  16. returntrue;
  17. }catch( e ){
  18. // localstorage容量不够,根据保存的时间删除已缓存到ls里的js代码
  19. if( e.name.toUpperCase().indexOf(\'QUOTA\')>=0){
  20. var item;
  21. var tempScripts =[];
  22. // 先把所有的缓存对象来出来,放到 tempScripts里
  23. for( item in localStorage ){
  24. if( item.indexOf( storagePrefix )===0){
  25. tempScripts.push( JSON.parse( localStorage[ item ]));
  26. }
  27. }
  28. // 如果有缓存对象
  29. if( tempScripts.length ){
  30. // 按缓存时间升序排列数组
  31. tempScripts.sort(function( a, b ){
  32. return a.stamp - b.stamp;
  33. });
  34. // 删除缓存时间最早的js
  35. basket.remove( tempScripts[0].key );
  36. // 删除后在再添加,利用递归完成
  37. return addLocalStorage( key, storeObj );
  38. }else{
  39. // no files to remove. Larger than available quota
  40. // 已经没有可以删除的缓存对象了,证明这个将要缓存的目标太大了。返回undefined。
  41. return;
  42. }
  43. }else{
  44. // some other error
  45. // 其他的错误,例如JSON的解析错误
  46. return;
  47. }
  48. }
  49. };

生成script标签注入到页面

生成 script 标签,append 到 document.head:

  1. var injectScript =function( obj ){
  2. var script = document.createElement(\'script\');
  3. script.defer =true;
  4. // Have to use .text, since we support IE8,
  5. // which won\'t allow appending to a script
  6. script.text = obj.data;
  7. head.appendChild( script );
  8. };

四、异步编程

basket.js 是一个典型的需要大量异步编程的库,所以稍有不慎,代码将会高度耦合,臃肿难看。。。

所以 basket.js 引入遵从 Promises/A+ 标准的异步编程库 RSVP.js 来这个问题。

(遵从 Promises/A+ 标准的还有 ES6 原生的 Promise 对象,jQuery 的$.Deferred 方法等)

所以 basket.js 中涉及异步编程的方法都会返回一个 Promise 对象。很好地解决了异步编程问题。例如 basket.require 方法就是返回一个promise 对象,因此需要按顺序加载 JS 的时候可以这样子写:

basket.require({url: \'helloworld.js\'}).then(function() {basket.require({url: \'helloworld2.js\'})
});

为了使代码更好看,basket.js 添加了一个方法 basket.thenRequire,现在代码就可以写成这样:

basket.require({url: \'helloworld.js\'}).thenRequire({url: \'helloworld2.js\'});

localStorage用法

localStorage

localstorage 必知必会

localstorage 必知必会

html5的localstorage详解

如何保存排序顺序?