常见的4种前端存储数据的方式是cookie,LocalStorage,session sorage,IndexedDB
其中sessionStorage有时效性,仅在当前会话有效,当前会话结束后就会删除.
所以实质上,用于长期存储的方式只有3种,
下面放出他们进行对比的表格
其中indexDB相当于本地数据库,所以单独拿出来说
对比项目 | cookie | localstorage | sessionStorage |
---|---|---|---|
数据存储时间 | 可设置失效时间 | 永久 | 仅当前会话 |
容量 | <=4kb | <=5mb | <=5mb |
其中三种方式都有原生api,其中cookie的原生api比较难用,cookie还有一个特点是会随着请求携带,并且可以被服务端随意修改
因此得出结论,一般网页应用用localstorage就行了,5mb存储一个应用的配置信息还是挺宽裕的.
另外会话中临时会使用的信息就用sessionStorage
cookie
cookie的主要属性表格
key | value |
---|---|
Name | cookie的名称 |
Value | cookie的值,最大容量4Kb |
Domain | cookie存储的域名 |
Path | cookie存储的路径 |
Size | cookie的大小 |
Expires | 过期时间,值可以是UTC格式,可以使用 Date.prototype.toUTCString() 进行转换 |
Max-Age | 优先级高于Expires,设置cookie存活的秒数 |
HttpOnly | 安全性,设置这个属性后,cookie就不会被js获取到,只有在发起请求时会带上 |
Secure | 不需要设置,当协议是https时,自动开启,指定浏览器只有在加密协议 HTTPS 下才能发送cookie\ |
SameSite | 跨站策略,设置为Lax,即仅允许同站或子站访问cookie;None:允许所有跨站cookie,设置为Lax会导致第三方cookie失效 |
cookie的原生api还是比较不人性化的,需要拼接字符串.所以一般我们会进行封装
下面是原生api的封装
设置cookie
/**
* 设置cookie
* @param {*} key 名称
* @param {*} val 值
* @param {*} time 失效时间
*/
export const setCookie = (key, val, expiresDays) => {
var date = new Date();
//将时间转换为cookie设置时间的格式
date.setTime(date.getTime() + expiresDays * 24 * 3600 * 1000);
document.cookie = key + "=" + val + ";expires=" + date.toDateString();
}
获取cookie
/**
* 获取cookie
* @param {*} key 名称
*/
export const getCookie = (key) => {
var getCookie = document.cookie.replace(/[ ]/g, "");
var arrCookie = getCookie.split(";")
var tips;
for (var i = 0; i < arrCookie.length; i++) {
var arr = arrCookie[i].split("=");
if (key == arr[0]) {
tips = arr[1];
break;
}
}
return tips;
}
删除cookie
可以直接调用setCookie,把cookie的失效时间设置为-1即可
setCookie(key,'',-1)
利用第三方cookie追踪用户
cookie执行同源策略
同源的定义为:如果两个 URL 的 protocol、port (en-US) (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。
下表给出了与 URL http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路径不同 |
https://store.company.com/secure.html |
失败 | 协议不同 |
http://store.company.com:81/dir/etc.html |
失败 | 端口不同 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html |
失败 | 主机不同 |
所以说如果在当前网站上你是无法设置和获取不同原的cookie的.
但是我们利用下面两个特性可以实现第三方cookie,追踪用户.
- 服务端可以设置cookie
- cookie会随着请求携带
举例一个利用第三方cookie的广告
假设a网站引入了一个c的广告组件,这个组件请求另一个网站c的服务端来设置cookie,这时你的浏览器上就有c的cookie了
这时你再访问同样引入c的广告组件的b网站,此时你浏览器上已经有访问a时c的cookie,因此能识别出你是同一个用户,从而给你显示一样的广告.
cookie 防止XSS攻击
如果我们的网站没有对用户输入进行过滤,就有可能造成xss攻击.
攻击者可以通过调用document.cookie获取后把我们的cookie发送给攻击者.
但是如果我们设置了HttpOnly属性后,document.cookie
就获取不到cookie了,提高了安全性
cookie会被利用于CSRF攻击
跨站请求伪造利用了cookie会被请求自动携带的特性.
比如你的电脑中有银行的cookie,你在打开银行的页面的同时,打开了一个钓鱼网站,这个网站包含对银行发起请求的图片标签,直接把你的一大笔钱转走了...因为浏览器在请求银行的时候会自动把cookie带上,这样银行的服务器就会以为是已经登录的用户的请求.
一般为了防止csrf,会增加其他的安全验证token.
或者直接不使用cookie,而使用localStorage,loacalStorage也会被xss利用,所以要注意对用户输入进行过滤.
localStorage和sessionStorage
这两个api都是随着html5加入的新api
这两者的主要区别是时效性不同,大小都为5mb
两者的api也是一样的
主要是以key value形式的字符串存储
缺点
localStorage的缺点是,只能存入字符串,无法直接存储对象,所以我们每次存入和取出都要重新json处理一下...
而JSON.stringify()我们知道,它对undefined funtion等一些类型无法正常处理,并且不能转换循环引用的对象,因此使用时需要注意.
存储storage
/**
* 存储Storage
*/
export const setStore = (params = {}) => {
let {
name,//名称
content,//内容
type,//类型
} = params;
let obj = {
dataType: typeof (content),
content: content,
type: type,
datetime: new Date().getTime()
}
if (type) window.sessionStorage.setItem(name, JSON.stringify(obj));
else window.localStorage.setItem(name, JSON.stringify(obj));
}
获取storage
/**
* 判断是否为空
*/
function validatenull (val) {
if (typeof val === 'boolean') {
return false
}
if (typeof val === 'number') {
return false
}
if (val instanceof Array) {
if (val.length == 0) return true
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true
} else {
if (val == 'null' || val == null || val == 'undefined' || val == undefined || val == '') return true
return false
}
return false
}
/**
* 获取Storage
*/
export const getStore = (params = {}) => {
let {
name,//名称
debug//是否需要转换类型
} = params;
let obj = {},
content;
obj = window.sessionStorage.getItem(name);
if (validatenull(obj)) obj = window.localStorage.getItem(name);
if (validatenull(obj)) return;
try {
obj = JSON.parse(obj);
} catch{
return obj;
}
if (debug) {
return obj;
}
if (obj.dataType == 'string') {
content = obj.content;
} else if (obj.dataType == 'number') {
content = Number(obj.content);
} else if (obj.dataType == 'boolean') {
content = eval(obj.content);
} else if (obj.dataType == 'object') {
content = obj.content;
}
return content;
}
删除storage
/**
* 删除localStorage
*/
export const removeStore = (params = {}) => {
let {
name,
type
} = params;
if (type) {
window.sessionStorage.removeItem(name);
} else {
window.localStorage.removeItem(name);
}
}
获取storage
/**
* 获取全部Storage
*/
export const getAllStore = (params = {}) => {
let list = [];
let {
type
} = params;
if (type) {
for (let i = 0; i <= window.sessionStorage.length; i++) {
list.push({
name: window.sessionStorage.key(i),
content: getStore({
name: window.sessionStorage.key(i),
type: 'session'
})
})
}
} else {
for (let i = 0; i <= window.localStorage.length; i++) {
list.push({
name: window.localStorage.key(i),
content: getStore({
name: window.localStorage.key(i),
})
})
}
}
return list;
}
清空全部storage
/**
* 清空全部Storage
*/
export const clearStore = (params = {}) => {
let { type } = params;
if (type) {
window.sessionStorage.clear();
} else {
window.localStorage.clear()
}
}
localStorage扩容
LOCALFORAGE是一个兼容性强的本地存储库,它的api类似于localStorage,
它对localStorage,webSQL,和indexedDB做了个平滑升级处理.
默认使用的是indexed和webSQL,在你的浏览器不支持这两个的时候才会降级使用localStorage
https://github.com/localForage/localForage
indexedDB
indexedDB是一个本地关系型数据库.和webStorage同期普及到浏览器的.
算是一个前端的终极本地数据存储方案
对比localStorage,有以下优势
- 存储量理论没有上限(实际上各个浏览器还是会进行一定的限制的)
- 支持异步操作,性能会更高.
- 原生支持存储js对象
- 是个数据库,功能强大
indexexDB最大的缺点是,api比较难用,功能繁琐.
一般我们用LOCALFORAGE(https://github.com/localForage/localForage)就行了,一定要用indexedDB的场合也比较少,如果这样通常不如做成客户端,我用sqlite不香吗