1. 前言
为了最大限度地保障同一浏览器同一域名下各个网页的用户对立,Web JS SDK 须要及时地将用户标识存入到 Cookie;
为了最大限度地缩小敞开页面导致的数据失落,Web JS SDK 将采集的数据存入到 localStorage 里进行批量发送,敞开页面未发送完的数据下次关上页面再次发送;
为了最大限度地保障可视化全埋点和网页热力求窗口关上的正确性,Web JS SDK 将相干的标识存入到 sessionStorage 里。
由此可见,存储数据是 Web JS SDK 的外围性能,上面逐个给大家介绍这三种存储形式。
2. 存储形式
2.1. Cookie
Cookie 实际上是一小段的文本信息(key-value 格局)。客户端向服务端发动申请,如果服务端须要记录该用户的状态,就应用 response 向客户端浏览器颁发一个 Cookie。如图 2-1 所示:
图 2-1 服务端应用 response 向客户端浏览器颁发一个 Cookie
客户端浏览器会把 Cookie 保存起来,当浏览器再次申请该网站时,浏览器把申请的网址连同 Cookie 一起提交给服务端。服务端查看该 Cookie,以此来识别用户状态。如图 2-2 所示:
图 2-2 浏览器把 Cookie 提交给服务端
Web JS SDK 中应用 Cookie 性能次要是用来存储前端变量。每当同一台设施通过浏览器申请集成 Web JS SDK 的页面时,就会读取曾经保留的 Cookie 值。
2.2. localStorage
localStorage 用于长久化的本地存储,没有过期工夫。除非被动删除数据,否则数据是永远不会过期的。
localStorage 提供的存储也是基于字符串的键值对。能够通过 setItem()、getItem() 来拜访其中的存储项。
2.3. sessionStorage
用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面能力拜访,并且当会话完结后数据也随之销毁。
因而,sessionStorage 不是一种长久化的本地存储,仅仅是会话级别的存储。浏览器网页敞开时,对应的 sessionStorage 就会被清空。
2.4. 三者的区别
三种存储形式的区别如表 2-1 所示:
3. Web JS SDK 中不同存储形式的利用场景
3.1. Cookie 的利用场景
SDK 初始化时从 Cookie 中读取用户标识,没有用户标识就生成新的 UUID,将新的 UUID 存储到浏览器 Cookie 中。
首次加载页面,Cookie 中无用户信息,将新生成的 UUID 保留到 Cookie 中。代码如下:
var uuid = _.UUID();
var cross = _.cookie.get(this.getCookieName());
cross = _.cookie.resolveValue(cross);
if (cross === null) {
// null 必定是首次,非 null,看是否有 distinct_id
sd.is_first_visitor = true;
this.set('distinct_id', uuid);
} else {
if (!_.isJSONString(cross) || !JSON.parse(cross).distinct_id) {sd.is_first_visitor = true;}
this.toState(cross);
}
首次加载集成 Web JS SDK 的页面,浏览器中 Cookie 的用户信息如图 3-1 所示:
图 3-1 浏览器中 Cookie 的用户信息
调用 sensors.login() 实现用户关联,将登录 ID 存储到 Cookie 中。distinct_id 为登录 ID,first_id 为匿名 ID。代码如下:
sd.login = function (id, callback) {
if (typeof id === ‘number’) {
id = String(id);
}
if (saEvent.check({ distinct_id: id})) {
var firstId = store.getFirstId();
var distinctId = store.getDistinctId();
if (id !== distinctId) {if (!firstId) {store.set('first_id', distinctId);
}
sd.trackSignup(id, '$SignUp', {}, callback);
} else {callback && callback();
}
} else {
sd.log('login 的参数必须是字符串');
callback && callback();
}
};
浏览器中 Cookie 的用户信息将更新成如图 3-2 所示:
图 3-2 浏览器中 Cookie 的用户信息更新
调用 sensors.logout() 实现用户退出登录,将 Cookie 中的 distinct_id 批改为匿名 ID,first_id 置空。代码如下:
sd.logout = function (isChangeId) {
var firstId = store.getFirstId();
if (firstId) {
store.set('first_id', '');
if (isChangeId === true) {var uuid = _.UUID();
store.set('distinct_id', uuid);
} else {store.set('distinct_id', firstId);
}
} else {
sd.log('没有 first_id,logout 失败');
}
};
浏览器中 Cookie 的用户信息将更新成如上图 3-1 所示。
调用 sensors.identify(“niming”,true) 批改匿名 ID。代码如下:
sd.identify = function (id, isSave) {
if (typeof id === ‘number’) {
id = String(id);
}
var firstId = store.getFirstId();
if (typeof id === ‘undefined’) {
var uuid = _.UUID();
if (firstId) {store.set('first_id', uuid);
} else {store.set('distinct_id', uuid);
}
} else if (saEvent.check({ distinct_id: id})) {
if (isSave === true) {if (firstId) {store.set('first_id', id);
} else {store.set('distinct_id', id);
}
} else {if (firstId) {store.change('first_id', id);
} else {store.change('distinct_id', id);
}
}
} else {
sd.log('identify 的参数必须是字符串');
}
};
登录状态下批改匿名 ID 后 Cookie 的更新如图 3-3 所示:
图 3-3 登录状态下批改匿名 ID 后的 Cookie
匿名状态下批改匿名 ID 后 Cookie 的更新如图 3-4 所示:
图 3-4 匿名状态下批改匿名 ID 后的 Cookie
调用 sensors.register() 注册的公共属性会写入 Cookie,在同域名的子页面之间共享。另外,Web JS SDK 渠道相干的属性也写入 Cookie。
Web JS SDK 通过在 Cookie 中保留变量来实现同域名下各个页面的变量共享。
3.1.1. 存储、获取和删除
Cookie 的存储、获取和删除不像 localStorage 有本人的 API 能够间接调用,Cookie 的这些操作都是基于 document.cookie 进行封装的。代码如下:
document.cookie = valid_name + ‘=’ + encodeURIComponent(valid_value) + expires + ‘; path=/’ + valid_domain + samesite + secure;
get: function (name) {
var nameEQ = name + '=';
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {var c = ca[i];
while (c.charAt(0) == ' ') {c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) == 0) {return _.decodeURIComponent(c.substring(nameEQ.length, c.length));
}
}
return null;
},
remove: function (name, cross_subdomain) {
cross_subdomain = typeof cross_subdomain === 'undefined' ? sd.para.cross_subdomain : cross_subdomain;
_.cookie.set(name, '', -1, cross_subdomain);
},
3.1.2. 属性项
如果没有在浏览器中设置过期工夫,Cookie 将会保留在内存中,生命周期随着浏览器的敞开而完结,这种被称为会话 Cookie;如果设置了过期工夫,浏览器就会把 Cookie 保留在硬盘上,敞开后再次关上浏览器这些 Cookie 仍然无效,直到过期工夫 Cookie 才会被革除。
具体的属性阐明如表 3-1 所示:
NAME=VALUE 键值对 Cookie 名称和 Cookie 值必须设置,这里的 NAME 不能和其余属性项的名字一样。
Expires 过期工夫,在设置的某个工夫点后该 Cookie 就会生效。
max-age 通知浏览器多长时间过期,单位是秒。不是一个固定的工夫点,max-age 的优先级高于 Expires。
Domain 生成该 Cookie 的域名,如 domain=”www.sensorsdata.cn”,根域或者子域。
Path 该 Cookie 是在以后的哪个门路下生成的,个别设置为 “/”,同一站点下所有页面都能够拜访这个 Cookie。
HttpOnly 告知浏览器不容许通过脚本 document.cookie 去更改这个值,同样这个值在 document.cookie 中也不可见。然而,在 http 申请中依然会携带这个 Cookie。留神:这个值尽管在脚本中不可获取,但依然在浏览器装置目录中以文件模式存在,这项设置通常在服务端设置。
SameSite 避免跨站申请伪造(CSRF)攻打和爱护用户隐衷。
Secure
如果设置了这个属性,那么只有通过 https 协定连贯时 Cookie 才能够被页面拜访。
表 3-1 Cookie 的属性阐明
3.1.3. 安全性
Cookie 存储在客户端浏览器里,在管制台下运行 document.cookie 就能够查看。因而,Cookie 存储的信息很容易被窃取。
基于平安的思考,须要给 Cookie 加上 Secure 和 HttpOnly,属性设置 HttpOnly=true,JS 无奈用 document.cookie 获取 Cookie 的内容,设置 Secure=true,Cookie 只能用 https 协定发送给服务端,用 http 协定是不发送的。
把 Cookie 的 Secure 设置为 true,只保障 Cookie 与服务端之间的数据传输过程加密,而保留在本地的 Cookie 文件并不加密。如果想让本地 Cookie 也加密,须要本人加密数据。Web JS SDK 给浏览器中 Cookie 的 value 值设置了加密。代码如下:
encrypt: function (v) {
return 'data:enc;' + _.rot13obfs(v);
},
decrypt: function (v) {
v = v.substring('data:enc;'.length);
v = _.rot13defs(v);
return v;
},
3.1.4. 优缺点
长处:
同一网站中所有页面共享一套 Cookie,能够实现同域名下跨页面的全局变量共享。例如:Web JS SDK 中的用户变量、公共属性变量、渠道参数变量;
兼容性强;
可灵便设置数据有效期。
毛病:
可能会被禁用。而 Cookie 被禁用会导致用户不能对立,采集的数据不精确;
安全性不高。关上控制台就能够查看 Cookie 信息,裸露用户隐衷;
存储空间小。存储数据大小不能超过 4K;
能够被用户手动清空。用户手动清空 Cookie 会导致:同一用户同一浏览器下拜访雷同的网页被辨认成不同的用户。
3.2. localStorage 的利用场景
因为浏览器的个性,敞开页背后发送申请有可能会失败。Web JS SDK 批量发送模式会把采集的数据存储到 localStorage 里,满足条件后合并发送,数据发送申请胜利后才会删除 localStorage 里的数据。因为敞开页面、网络状态等起因导致数据发送申请失败,不会删除 localStorage 里的数据。当刷新页面或进入同域名的新页面时,如果网络条件恢复正常,会持续发送缓存的数据。这样以来,能够大大地缩小数据失落概率。代码如下:
writeStore: function (data) {
var uuid = String(_.getRandom()).slice(2, 5) + String(_.getRandom()).slice(2, 5) + String(new Date().getTime()).slice(3);
localStorage.setItem('sawebjssdk-' + uuid, JSON.stringify(data));
}
readStore: function () {
var keys = [];
var vals = [];
var obj = {};
var val = null;
var now = new Date().getTime();
var len = localStorage.length;
var pendingItems = this.getPendingItems();
for (var i = 0; i < len; i++) {var itemName = localStorage.key(i);
if (itemName.indexOf('sawebjssdk-') === 0 && /^sawebjssdk\-\d+$/.test(itemName)) {if (pendingItems.length && _.indexOf(pendingItems, itemName) > -1) {continue;}
val = localStorage.getItem(itemName);
if (val) {val = _.safeJSONParse(val);
if (val && _.isObject(val)) {
val._flush_time = now;
keys.push(itemName);
vals.push(val);
} else {localStorage.removeItem(itemName);
sd.log('localStorage- 数据 parse 异样' + val);
}
} else {localStorage.removeItem(itemName);
sd.log('localStorage- 数据取值异样' + val);
}
}
}
return {
keys: keys,
vals: vals
};
},
触发事件就写入 localStorage,控制台可查看 localStorage 存储的数据,如图 3-5 所示:
图 3-5 控制台查看 localStorage 存储的数据
留神:基本上能够认为绝大多数支流浏览器都曾经反对了 localStorage,而 IE 是个特例。尽管 IE8 反对了 localStorage,然而更低版本的 IE 都不反对,对于不反对 localStorage 的浏览器默认实时发送。
localStorage 也能够像 Cookie 一样存储变量,Web JS SDK 的网页热力求剖析中就应用 localStorage 来存储 jsonp 标识,页面加载时依据 localStorage 里有无这个标识判断用 ajax 形式还是 jsonp 形式申请数据。
3.2.1. API
3.2.1.1. 查看
相似 Chrome、Safari 这样的浏览器能够间接关上开发者工具,在 application 下的 localStorage 里间接查看数据。而 IE 须要在控制台中输出 localStorage 来查看数据,如图 3-6 所示:
图 3-6 IE 在控制台中输出 localStorage 来查看数据
3.2.1.2. 存储
应用 localStorage 的时候,咱们须要判断浏览器是否反对 localStorage 这个属性,调用 localStorage.setItem() 来存储数据。代码如下:
if(! window.localStorage){
alert("浏览器不反对 localstorage");
return false;
}else{
localStorage.setItem('sensors_heatmap_method', 'jsonp')
}
3.2.1.3. 获取
localStorage 依据 Item 获取采集数据的值,例如:JSON.parse(localStorage.getItem(“sawebjssdk-3465518851230600”)),获取的值为 String 类型,须要本人转换成对象。如图 3-7 所示:
图 3-7 localStorage 依据 Item 获取采集数据的值
3.2.1.4. 删除
革除 localStorage 某条信息,例如:localStorage.removeItem(“sawebjssdk-3465518851230600”),示意革除 sawebjssdk-3465518851230600 这一条数据。
革除 localStorage 保留对象的全副数据:localStorage.clear()。如图 3-8 所示:
图 3-8 革除 localStorage 保留对象的全副数据
3.2.2. 优缺点
长处:
可能长期大量地保留浏览器的数据。Web JS SDK 触发的事件能够保留到 localStorage 里,除非手动删除否则不会失落;
存储的数据不会被动发送到服务端;
更多丰盛易用的接口。
毛病:
协定名、主机名和端口名都必须雷同能力共享 localStorage。这点与 Cookie 不同:雷同的域名不同的协定 Cookie 会共享,但 localStorage 不共享;
保留的数据超出了浏览器的大小,不会被动删除数据但控制台会报错;
兼容性不强,IE8 以下版本不反对,在挪动设施上的浏览器或 Native App 用到的 WebView 里,localStorage 都是不牢靠的。可能会因为各种起因(例如:退出 App、网络切换、内存不足等)被清空,Android 里用到的 WebView 必须手动开启 localStorage,否则是不反对的;
浏览器隐衷模式下不反对 localStorage 的读取;
安全性低,容易被恶意代码注入。
3.3. sessionStorage 的利用场景
Web JS SDK 可视化全埋点就是通过应用 sessionStorage 来存储和读取 sensors-visual-mode 标识,判断网页是可视化全埋点模式还是采集模式。代码如下:
设置标识:sessionStorage.setItem(‘sensors-visual-mode’, ‘true’)
获取标识:sessionStorage.getItem(‘sensors-visual-mode’)
删除标识:sessionStorage.removeItem(‘sensors-visual-mode’)
Web JS SDK 网页热力求也是通过用 sessionStorage 来存储和读取 sensors_heatmap_url 和 sensors_heatmap_id 两个标识来判断网页是网页热力求模式还是采集模式。浏览器中查看 sessionStorage 的形式如图 3-9 所示:
图 3-9 浏览器中查看 sessionStorage 的形式
网页热力求和可视化全埋点都是通过指定地位关上网页,将对应的标识存入到 sessionStorage 里。如果不是通过指定地位关上,雷同的地址新关上一个窗口为新的会话。因为 sessionStorage 在不同会话中是不共享的,所以雷同的地址关上的页面为采集模式。
3.3.1. API
sessionStorage 中的 API 与上文中的 LocalStorage 用法一样,这里不再赘述。
3.3.2. 优缺点
长处:
可能大量地保留浏览器中的数据;
在不同的窗口之下的 sessionStorage 存储互相独立、互不烦扰。
毛病:
兼容性不强,IE8 以下版本不反对;
安全性低。
4. 总结
置信通过本文,咱们对浏览器数据存储有了一个大抵的理解。没有一种浏览器存储数据形式是完满的,都有肯定的优缺点。因而,面对不同的需要时,咱们要抉择不同的形式来存储前端数据:
如果存储的数据量较小,用于服务端申请,利用的网页范畴较广,能够应用 Cookie 存储;
如果存储的数据量较大,数据不用于进行服务端申请,并须要永恒保留,能够应用 localStorage 存储;
如果存储的数据量较大,敞开窗口后数据就清空,雷同域名不同会话之间数据不共享,能够应用 sessionStorage 存储。
须要留神的是:不是什么数据都适宜存储到 Cookie、localStorage 和 sessionStorage 中的。因为它们保留在本地容易被篡改,应用它们的时候须要留神是否有恶意代码注入的危险,所以千万不要用它们存储敏感数据。
文章来自公众号——神策技术社区