一、a.meituan.com 和 b.meituan.com 这两个域可以共享同一个 localStorage 吗?javascript
二、在 webview 中打开一个页面:i.meituan.com/home.html,点击一个按钮,调用 js 桥打开一个新的 webview:i.meituan.com/list.html,这两个分属不一样 webview 的页面能共享同一个 localStorage 吗?css
三、若是 localStorage 存满了,再往里存东西,或者要存的东西超过了剩余容量,会发生什么?html
答案前端
一、同一个域名(document.domain)共享同一个 localStorage,a.meituan.com 和 b.meituan.com 是两个域名,因此不能共享java
二、能。至关于同一个浏览器的不一样标签页。不一样浏览器之间不能共享。ios
三、存不进去并报错(QuotaExceededError)web
在大公司,同一个域名下可能存在几十上百条业务线,每条业务线均可能由于各类理由往 localStorage 里塞东西,跨页面传数据啦、缓存啦、离线化啦、性能优化啦...,5M 看起来不少,其实很快就用完了。而开发时基本无感知,是由于你们都只访问本身的业务,但用户会访问各类业务,时间一久,很容易就存满了,凡是严重依赖 localStorage 的业务流程都存在风险,写可能出问题,读天然也会出问题。面试
一种容易想到的方案是,当 localStorage 存满后降级到 sessionStorage 里。看上去没啥问题,但实际业务中 app 内 h5 页面跳转经常采用新打开 webview 的方式,这么作的好处是关闭一个 webview 能够直接回到上一个页面,而不用从新加载页面,对于订单填写这类带有状态的页面就很须要这么作。新打开 webview 等于新打开一个会话,而 sessionStorage 只能存在于同一个会话中,所以 sessionStorage 没法跨页面共享。后端
那降级到 cookie 里呢?cookie 一共才 50 个,总大小不超过 4k,做为 backup 过于脆弱,并且还会影响请求的效率。若是后端对请求头大小作了限制,还可能产生 413 错误,致使请求被拦截。浏览器
那降级到 url 上呢?很麻烦。好比有一个交互流程是这样的:页面 A => 页面 B => 页面 C,若是页面 A 的数据要传到页面 C,就得经过页面 B 作一层中转。并且 url 长度也是有限制的。
单页应用解决跨页面传数据就很简单,改形成单页应用呢?这个就得估算成本,看老板们认不承认了,并且原有应用积累了大量的业务逻辑,没有注释,没有测试用例,需求文档散落在不知名的角落,你真能保证从新作的和原来的功能如出一辙吗。
咱们还能够求助客户端同窗,经过 js bridge 提供一个仿 localStorage 的东西,不过要考虑版本的问题,新版 app 里使用了客户端提供的 store,怎么兼顾老版 app,并且还要考虑兼容浏览器、微信。因此这种方案也只能解决一部分问题,固然,若是 h5 的流量绝大多数都在 app 里,那么这种方案是能够解决一大部分问题的,不过客户端提供的存储可不见得比原生的存储可靠,仍是得加 backup。
咱们还能够求助后端同窗,多加几个字段甚至多加几个接口,不过这涉及到核心业务流程的改造,风险不小,并且不见得能彻底解决问题,也没法永久的解决问题。
咱们还能够来一招互相伤害大法,那就是把别人存的东西都删掉。。。
localStorage 是个好东西,不用,这是因噎废食,用,又很难统一和约束各业务线的用法,一旦放开用,就总会面临存满的风险。跟你在同一个域名下作开发的人可能跟你不在同一栋楼,甚至可能不在同一个城市,你有那个影响力去统一全部人的使用规范吗。
还有一个很讨厌的事情:safari 在隐私模式下不支持 localStorage 的存取(ios11 如下),这种状况比较罕见,但若是出了客诉,也是个大坑。
localStorage 归根结底就两个做用:持久化存储与跨页面传数据。持久化存储不会出问题,存不进去就存不进去呗,取不出来就去其它地方取,或者不取。问题就出在跨页面传数据上,上一个页面由于 localStorage 存满致使数据没有写入,下一个页面读取数据为空,从而致使错误。
同一个域名共享同一个 localStorage,而同一个域名下存在过多独立的业务线,业务线之间各自为政,毫无节制的攫取公共资源,这就是 localStorage 溢出问题的根源。
就我观察的状况来看,不少公司都喜欢把 h5 页面挂在 i.xxx.com 或 m.xxx.com 下,而后经过路径划分业务,好比 i.xxx.com/project-a, i.xxx.com/project-b...,随着业务发展,愈来愈多的业务都加到 i.xxx.com 中,“公地悲剧”就迫不得已的产生了,并且积重难返。我之前在的团队也是如此,用 h五、js、css 这样的类型名称来划分目录,初期东西少,天然没问题,但后来全部应用都把资源塞到 js 文件夹、css 文件夹下,一个文件夹包含了来自五湖四海的上百个文件,维护起来十分难受。
经过应用类型划分,而不是经过业务类型划分,这是最初架构策略的问题。若是 a 业务挂在 a.xxx.com 下,b 业务挂在 b.xxx.com 下,每一个业务有独立的团队维护,localStorage 从公共资源变成团队的私有财产,或许这样才能从根源上解决 localStorage 无限膨胀的问题。有网友提出对 i.xxx.com 进一步划分子域,其实也是这个思路。
假设咱们回到起点,从零建设前端工程,咱们怎么避免 localStorage 存满的问题?
一、划分域名。各域名下的存储空间由各业务组统一规划使用
二、跨页面传数据:考虑单页应用、优先采用 url 传数据
三、最后的兜底方案:清掉别人的存储
在已然发展好久的业务中,咱们怎么解决此问题呢?
const QUOTA_EXCEEDED_ERR_CODE = 22 function write (key, data) { try { localStorage.setItem(key, data); } catch (e) { if (e.code === QUOTA_EXCEEDED_ERR_CODE) { localStorage.clear(); localStorage.setItem(key, data); } } }
上面这个方法仍是有点问题,由于它把本身业务要用的东西也给删了,因此本身的业务最好统一在 key 上加一个前缀,清空 localStorage 时只删别人的。
有的同窗可能会担忧,这样会不会对其它业务形成伤害?或者产生一些难以发现的 bug。其实这种担忧很大程度上是由于忽略了实际的使用场景。用户用同一个设备打开同一个 app,在同一个时间只能访问一个业务,所以不会存在某个业务正在使用过程当中,localStorage 被其它业务清掉的场景,除非!除非有交叉的业务场景。
没有银弹,没有十全十美的方案。
掘金网友@FE 提出用 indexedDB 存文件类型的数据,localStorage 存业务数据,这是一种能够缓解问题的方案。