在前端面试中,storage是面试官经常问的问题,我先问你几个问题,如果你回答不上来,那么你应该阅读一下:知道storage吗?storage存储的数据类型有什么?sessionStorage的生命周期?你都用localStorage做一些什么事?localStorage容量限制是多大?了解indexedDB跟WebSQL吗?如果你要在前端缓存视频或者图片怎么实现?

目录

1、使用localStorage:

2、使用sessionStorage:

3、使用Cookie

4、使用IndexedDB跟Web SQL


在我做网站的时候,由于服务器带宽有限(仅仅3M),一开始打开视频页面和音乐页面会非常卡,经常造成接口请求超时,因此必须在前后台都加入缓存机制。其实我们使用的很多软件都有缓存机制,大部分人接触的基本都是后台的缓存中间件redis等等,它可以有效的减轻数据库和服务器的压力。前端缓存主要着重用户体验,实现一些特定功能(如记住上一次浏览),为用户节省不必要的流量浪费,减少QBS带给服务器的压力。本文章主要针对前端的浏览器缓存Storage(localStorage,SessionStorage)、IndexedDB(WebSQL)、Cookie,谈谈我个人的理解和实现,理解可能有误,请多指正。

本文主要内容有:localStorage存储,sessionStorage存储图片对象,IndexedDB存储音乐(实现听过的音乐0流量播放)。

注意:本篇描述的是手动管理缓存,因此不会使用下面给Header添加标签的缓存技术。

<meta http-equiv="Cache-Control" content="public" />
<meta http-equiv="Pragma" content="public" />
<meta http-equiv="Expires" content="36000" />

这是因为,几乎所有网站都不会开启强缓存,甚至会禁用强制缓存...

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

其实,目前浏览器是有自动缓存机制的,它都是存储的静态资源。在Tomcat和JSP的年代,这个自动缓存让前端简直吃屎,甚至手动清理浏览器缓存跟重启Tomcat都不一定清的掉。笔者之前在页面引了一张图片,然后发现不好看,就用另一张图片替换了,结果总是展示之前那张图片。其实在静态资源后面加入时间戳机制或一个方法等可以有效阻止此类缓存,这类机制将导致页面会重新请求静态资源,从而避免了使用缓存的静态资源,如:

<script src="js/zq.js?v=123"></script>
<script src="js/zq.js?time=<%=System.currentTimeMillis();%>"></script>

这些自动缓存机制我们完全不必在意这些是如何实现的。下面我就介绍以下前台需要我们手动操作的缓存技术及API。

拿我音乐demo页面为例(demo链接):

首先,Storage里面都是存储的键值对,必须注意这个值必须是字符串,如果你存的是1、true,那么取出来就是“1”跟“true”,而且不能将undefined存入,如果要存储对象必须要用JSON.stringify()强转字符串存入。目前localStorage在各个浏览器存储容量各有不同,对于谷歌通常一个值最大是5M,其他浏览器得用插入字符串的方式,用trycache获取溢出异常来获取最大存储容量。

1、使用localStorage:

上图的内容都是存储在localStorage里的内容。localStorage应用最多最广泛,几乎所有的网站在localStorage都会满满的存进一堆东西。这是因为几乎很多的网站都是允许用户不登录就可以浏览的,那么由于用户没有登陆,这些网站的后台就记录不了该用户的行为。记录用户行为的重任就落在localStorage身上了。localStorage的生命周期是理论永久,而且是不可能被爬虫爬到的,如果用户或者代码不去手动清理它,它会一直存在。至于存储什么内容,得看你想怎么做了,据我观察,各大网站一个共识就是存储用户信息、主题(通常是夜间日间模式)、搜索历史、上次播放的位置等等,我的音乐网站就存储了音乐播放时间(就是视频的从上次观看的时间播放)、次数、播放历史、是否开启引导等等(有趣的是,百度文档就把浏览限制存到这里面,如果你浏览百度文档被限制继续查看,请把localStrage内容清空刷新页面)。

localStorage的API非常简单,通常会用到localStorage.setItem("key","value")设置键值对,用到localStorage.getItem("key")来获取key对应的value字符串,如果不存在就会返回null而不是undefined,使用localStorage.removeItem("key")去移除key对应的localStorage,使用localStorage.clear()去清空localStorage。例如:

<script>console.log("取localStorage的值name:"+localStorage.getItem("name"));//第一次取肯定不存在,此时返回nulllocalStorage.setItem("name","hoppinzq.com");//存一个值console.log("取localStorage的值name:"+localStorage.getItem("name"));//存了才能取出来,返回hoppinzq.comlocalStorage.removeItem("name");//删除 或者localStorage.clear()console.log("取localStorage的值name:"+localStorage.getItem("name"));//已经被删了,返回nulllocalStorage.setItem("name","hoppinzq.com");//存一个值</script>

是不是很简单呢?看看我的网站是怎么写的吧

if (typeof(Storage) !== "undefined") { //浏览器支持Storage或隐私模式吗?if (localStorage.getItem("historySongs")) {  //先看看历史歌单的键值是否已创建historySongs = JSON.parse(localStorage.getItem("historySongs")); } else { //历史歌单不存在时,一般用户第一次进到你页面(或者请过缓存)肯定不存在,所以需要加这个判断historySongs = [];}var isE = false;//这里是判断在听的歌曲是否包含在历史歌曲里,不用管for (let historySong of historySongs) {if (historySong.id === music_id) {isE = true;break;}}if (!isE) {historySongs.push(e.jPlayer.status.media);localStorage.setItem("historySongs", JSON.stringify(historySongs));//设置历史歌曲}
}

2、使用sessionStorage:

上面的内容是我的网站的单页在sessionStorage存储的东西,有各种请求信息,图片等等。首先sessionStorage就像后台的session作用域,它的一次生命周期是一次临时会话,用户一旦刷新页面或者重新打开页面等执行路由刷新页面的操作都会导致sessionStorage内的内容清空 ,因此使用场景很受限,但是对于很多后台管理系统的前台来说简直是个宝。我观察了很多大型网站基本没有使用sessionStorage的,我主要是用来存储一些特定请求(比如分页,因为该请求不需要刷新页面)和媒体图片这样的资源,因为作为文件一般而言是最少被修改(忽略那些天天改头像的)。

如上面动图,在进入第二页的时候会请求拿到这些图片的路径,加载图片会花费大部分时间和占用很多带宽,而返回第一页的时候基本是秒加载,不使用任何流量,流畅到就好像做了个假分页。这样看来sessionStorage对于图片多的页面非常友好,因为图片大小很小,但对于大的媒体资源就好像无能为力了。

sessionStorage的API很简单,跟localStroage一样。下面就看看我的网站是怎么写的吧。

/*** 缓存网易云图片* 注意:第一次加载的图片不建议直接使用缓存的图片,因为要花更多时间,第二次使用即可* @author zq* @param {Object} wyyUrl 图片url :格式 :* https://p3.music.126.net/t_Ax6p2zxfsBkymFbk6fMA==/109951165985230942.jpg*/
function cacheNetBaseCloudImg(wyyUrl) {//这里解析了一下,为了保证sessionStorage的键不重复而且通过图片id能拼接出来let cacheImageNameIndex_1 = wyyUrl.lastIndexOf("/") + 1;let cacheImageNameIndex_2 = wyyUrl.lastIndexOf(".");let cacheImageName = "image_" + wyyUrl.substring(cacheImageNameIndex_1, cacheImageNameIndex_2);var StorageCacheImageUrl;//先看看要加入缓存的图片是否已经在缓存里了StorageCacheImageUrl = sessionStorage.getItem(cacheImageName);if (StorageCacheImageUrl != null) {//如果缓存里有该图片,直接返回缓存的图片URL对象return StorageCacheImageUrl;} else {//使用原生ajax获取图片URL对象(或者base64)var xhr = new XMLHttpRequest();var blob;xhr.open('GET', wyyUrl, true);xhr.responseType = 'blob';xhr.onload = function() {var data = xhr.response;blob = new Blob([data]);// var reader = new window.FileReader();// reader.readAsDataURL(blob); // reader.onloadend = function() {//     localStorage.setItem(cacheImageName, reader.result);//reader.result就是图片的Base64码// }var blobUrl = window.URL.createObjectURL(blob);//创建图片的URL对象cacheName.push(cacheImageName);sessionStorage.setItem(cacheImageName, blobUrl);return blobUrl;};xhr.send();}
}

观察上面代码注意以下几点:

1、我是保存的图片URL对象,就如同java一样,当页面刷新关闭的时候会执行dom卸载操作跟垃圾回收,这个URL对象就被垃圾回收了失去价值了,所以sessionStorage存的东西实际没什么用处了,因此必须给页面绑定beforeunload事件手动清除sessionStorage内的图片URL,其实页面刷新的时候会自动清除,但是。。讲究!!!

$(window).on("scroll", function() {//绑定滚动事件。。。}).on("beforeunload", function() { //给页面刷新跟关闭绑定事件,手动清除缓存,并存储用户最后一次搜索的内容localStorage.setItem("lastSearch", $("#search").val());sessionStorage.clear();//清除sessionStorage内容,防止出问题
});

2、在上面代码cacheNetBaseCloudImg方法的注释里有一个获取图片base64码的代码,你可以存储base64码到localStorage里面,下次直接丢到img标签也可以。但是我不建议这么干,因为一来图片太多不好管理,得加入时间戳机制去隔一段时间清理一下(比如允许用户一个月只能修改一次头像),不然的话很可能就导致头像不同步了;二来万一图片很大就会溢出导致存不进去。那么如何存储大文本文件、媒体文件(音乐视频)呢?

其实现有的Storage也是能实现的,思路就用链表的思路,我们只需要先创建一个存储地址的字符串,然后把大文本文件转成base64码分几批存入localStorage,每一批存储一定量base64码和指向上一个localStorage地址的字符串,读取的时候拼起来就行。这个思路有很多,反正你能把分割的base64码正确还原即可。由于代码太多就不展示,有兴趣可以访问demo链接,或者粘贴下载文件 到浏览器上下载源代码。

3、使用Cookie

其实cookie这个东西在前端讲有点尴尬,因为cookie一般都是后台创建的并赋予它的生命周期(前台也能创建,有一个很好用的谷歌的cookie.js,但是本人前端目前没用过,以后会补充)。它的生命周期并不像localStoage一样是永久的,也不像sessionStoage是一次临时会话,你可以存一些东西而且完全不必手动去清理它。前端去创建cookie并存储数据我暂时没用过,因为我对于捕获用户行为跟喜好并没有太多思路,我都是是在后台操作。就是帮你记住你的用户名。大多数是用户登录后台会创建cookie去存储用户登录的唯一标识token。

通过ajax可以携带cookie的特点,后台通过校验cookie的内容来辨别请求是登录用户发起的还或是伪造的。

//ajax
xhrFields: {withCredentials: true //这个配置项就是请求携带cookie
}
//axios
withCredentials: true
//Fetch
fetch(url, { credentials: 'include' })

4、使用IndexedDB跟Web SQL

首先这两个东西可能需要前端稍微了解一下数据库表和一点点sql的知识,Web SQL底层是SQLlite,但是除了谷歌都不支持。indexedDB跟sql语句没什么关系,而且比较难用,但它的value可以存储任何数据类型包括undefined,基本没有容量限制(这取决电脑跟手机),因此我们用这个存储媒体blob对象或者base64比较好。所以为了照顾前端,就找到了一个开源的js组件localforage.js(下载链接:下载文件 )(中文文档地址:localForage 中文文档)。该组件将IndexedDB封装的很简单,API也很贴心的仿照了Storage的API。

下面我们用localforage.js将音乐存入IndexedDB中,真正实现一次加载以后就可以不用流量播放。

首先分析:网易云音乐的url是http://music.163.com/song/media/outer/url?id=22766042.mp3,也就是?id=音乐id.mp3。

可以看到,该链接返回302响应码,并重定向到了真正的音乐的接口。我们首先要获取的是音乐文件的blob对象,所以我们得用ajax去请求这个接口, 但是很蛋疼的,ajax请求不支持重定向,因为通过ajax捕获重定向的url是有一定风险的,有可能你接收的是流,抑或是另外的重定向(淘宝跟拼多多就很喜欢套娃),responseType就很难声明了。所以我们要在后台通过HttpCilent模拟这个请求,关闭重定向,并捕获302重定向响应码,将真正的音乐url返回给前台:

/*** 获取重定向后的URL* @author:ZhangQi**/
@RestController
@RequestMapping("/music")
public class MusicController {@GetMapping("/getMusicRealUrl")public ApiResponse getMusicRealUrl(String url){HttpClient client = new DefaultHttpClient();HttpParams params = client.getParams();params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);//禁用自动重定向HttpContext context = new BasicHttpContext();HttpGet get = new HttpGet(url);try {HttpResponse response = client.execute(get,context);HttpHost host = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);HttpUriRequest request =  (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);int code = response.getStatusLine().getStatusCode();if(code==302){HttpEntity entity = response.getEntity();if(null != entity){Header[] headers = response.getHeaders("Location");if(headers!=null && headers.length>0){String redirectUrl = headers[0].getValue();return ApiResponse.data(redirectUrl);}}}else{return ApiResponse.fail(-1,"不是重定向的页面");}} catch (ClientProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}
}

前台将音乐blob对象放入缓存代码:

        /*** 缓存网易云音乐* @author zq* @param {Object} musicURL* url格式:http://music.163.com/song/media/outer/url?id=22766042.mp3*/function cacheNetBaseCloudMusic(musicURL) {let cacheMusicNameIndex_1 = musicURL.lastIndexOf("=") + 1;let cacheMusicNameIndex_2 = musicURL.lastIndexOf(".");let cacheMusicName = "music_" + musicURL.substring(cacheMusicNameIndex_1, cacheMusicNameIndex_2);localforage.getItem(cacheMusicName, function(err, value) {if (value == null) {//获取真正的网易云播放url$.get("http://hoppinzq.com/music/getMusicRealUrl?url=" + musicURL,         function(data) {if (data.code == 200) {let realUrl = data.data;var xhr = new XMLHttpRequest();var blob;xhr.open('GET', realUrl, true);xhr.responseType = 'blob';xhr.onload = function() {var data = xhr.response;blob = new Blob([data]);localforage.setItem(cacheMusicName, blob);};xhr.send();}})} else {return value;}});}

从代码看出,localforage的API跟Storage如出一辙(中文文档地址localForage 中文文档)也是getItem,setItem(),removeItem(),clear()。但是贴心的设计了回调函数如localforage.getItem("key",function(err, value){},

最后,将缓存里的blob对象解析成URL对象放到音乐播放列表就行了,大家可以在demo里断网体验一下哦:

        /*** 动态设置播放列表* 注意:在执行setPlaylist方法会执行init方法,该方法会把当前播放时间currentTime置为0,这会导致音乐重新从列表第一首开始重新播放* 而原来你在听的歌曲会停掉,我的做法是:在播放时就建立一个被静音的播放源,在执行此法时将静音去掉,此时声音会增大,这是因为原有* 的播放源的音量使用的系统音量乘以用户拖动的音量百分比,在切换播放源时会立即使用系统音量因此要手动×上一个比值。* @param {Object} songList*/function setMusicList(songList) {timeout(1).then(function() {//遍历歌曲列表,将存在缓存的歌曲的URL对象替换网易云URL$.each(songList, function(index, value) {localforage.getItem("music_" + value.id, function(err, value_) {if (value_ != null) {var url = window.URL.createObjectURL(value_);value.mp3 = url;}});})//微微的缓冲return timeout(1000);}).then(function() {myAlert("切换声音源中。。。");myPlaylist.setPlaylist(songList);audio_hidden.muted = false;var wi = parseFloat($(".jp-volume-bar-value").width() / $(".jp-volume-bar").width()).toFixed(2);audio_hidden.volume = wi;});}

这个地方作为一个纯前端是不太好理解的,因为现在直接访问文件资源的接口是一定要做重定向并且携带cookie、时间戳、加密的(网易云音乐真实URL是根据时间戳自动生成的,写错一个字母,你都会吃到网易云4xx黑名单套餐)。现在很少网站能做到让你免登录下载的(本站就是免登录>-<),因为这些接口会直接返回流,此类接口一旦被盗刷,频繁的IO操作会严重降低服务器性能并造成带宽堵塞。最后可能导致其他用户根本进不到我的网站,甚至可能导致服务器宕机。因此,必须要做一个重定向,并在重定向前对接口进行有效的管控。

具有很多手动缓存页面的源代码:链接

在线访问:链接

我的网站心得之缓存技术(前端篇)相关推荐

  1. 网站性能优化总结(前端篇)

    1.网络传输性能优化 这是navigation timing监测指标图,从图中我们可以看出,浏览器在得到用户请求之后,经历了下面这些阶段:重定向→拉取缓存→DNS查询→建立TCP链接→发起请求→接收响 ...

  2. 前端cookie、localStorage、sessionStorage缓存技术总结

    前端cookie.localStorage.sessionStorage缓存技术总结 转载自:https://www.cnblogs.com/belove8013/p/8134067.html 1.C ...

  3. 网站缓存技术总结( ehcache、memcache、redis对比)

    网站技术高速发展的今天,缓存技术已经成为大型网站的一个关键技术,缓存设计好坏直接关系的一个网站访问的速度,以及购置服务器的数量,甚至影响到用户的体验. 网站缓存按照存放的地点不同,可以分为客户端缓存. ...

  4. 前端性能优化之缓存技术

    缓存一直以来都是用来提高性能的一项必不可少的技术 , 利用这项技术可以很好地提高web的性能. 缓存可以很有效地降低网络的时延,同时也会减少大量请求对于服务器的压力. 接下来这篇文章将会详细地介绍在w ...

  5. 网站提速-缓存技术(4)

    缓存技术 memcached基本概念 Memcached是danga的一个项目,最早是为LiveJournal 服务的,最初为了加速 LiveJournal 访问速度而开发的,后来被很多大型的网站采用 ...

  6. 【前端页面缓存技术方案】

    前端页面缓存技术方案 关于页面缓存数据的纯前端技术方案 背景 项目存在的现有方案 思考

  7. 动易swCMS6.5网站的缓存技术分析

    动易swCMS6.5网站的缓存技术分析 动易swCMS6.5网站的缓存技术原理: 将网页的Html内容作为缓存的数据 保存在服务器的Application变量中 用来保存缓存数据的Applicatio ...

  8. 为什么要使用页面缓存技术

    为什么要使用页面缓存技术 一.总结 一句话总结: 系统的瓶颈往往是来自于数据库,我们可以使用缓存来减少对数据库的访问! 系统都是逐渐演进的,一个系统在运行中必须是根据场景逐渐地提高优化性能.高并发就是 ...

  9. Web前后端缓存技术

    Web缓存技术 一.缓存概述 缓存原本是一个硬件的概念:缓存就是数据交换的缓冲区(称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,如果找到了则直接执行,找不到的话则从内存中找. ...

  10. 详解PHP操作Memcache缓存技术提高响应速度的方法

    一般来说,如果并发量不大的情况,使不使用缓存技术并没有什么影响,但如果高并发的情况,使用缓存技术就显得很重要了,可以很好的减轻数据库和服务器的压力,当然解决高并发的技术有很多,这里只是以缓存的角度来说 ...

最新文章

  1. 深度学习的核心:掌握训练数据的方法
  2. html动画图片重叠,不一样的层叠照片动画-ANIMATED CSS3 PHOTO STACK
  3. java中的slave_java – Jenkins slave在构建过程中脱机
  4. 三星会使用华为的鸿蒙系统,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  5. 让用户闭着眼睛买东西可还行?雷军谈小米的目标...
  6. c语言可变参数 printf,c语言 使用可变参数列表实现printf(my_printf)
  7. RefreshParenwin 打开子窗体 并回发
  8. html怎么命令打开文件夹下,dos下打开文件夹命令 怎么用dos命令进入指定的文件夹...
  9. 有趣有用的PCA——PCA压缩图片
  10. python监听键盘事件pyhook用法_python 监听键盘事件pyHook
  11. 一键解决Windows聚焦不更新的方法
  12. 简信CRM:什么样的企业适合引入CRM管理系统?
  13. 在代码里设置view的android:layout_marginTop
  14. QT 设置label内字体,字体颜色,背景色 ; 字体的中英对照
  15. 3dMax 灯光(街道的白天与夜晚)
  16. 博弈论_奇偶性、单向行走
  17. c语言 注册商标标志,什么是标识符?
  18. 开源bug管理工具-Cynthia
  19. signature=a6cb6ce8d8e12b283f4fc618f250a727,MS12-041:Windows 内核模式驱动程序中的漏洞可能会允许特权提升:2012 年 6 月 12 日...
  20. skype客户端搜不到联系人解决办法

热门文章

  1. Python人眼视线追踪系统
  2. OpenCV——调整图片的对比度、亮度和饱和度
  3. 蓝桥杯试题 基础练习 圆的面积
  4. 手把手教学,详细的外挂制作教程视频
  5. python太阳花画法_Python——教你画朵太阳花
  6. 记录第二次进行的助教培训-评分
  7. ffmpeg音频格式转换
  8. Win10喇叭图标出现红叉,没有声音,并且提示“未安装任何音频输出设备“的解决办法
  9. 共享文件计算机和设备拒绝访问,“拒绝访问”共享文件解决方法
  10. 工控组态编程相关知识点介绍