Cookie 直译过来其实是“小甜饼”。但是在互联网的世界里,Cookie 是 Web 开发中一种常用的数据存储、会话跟踪技术。
Cookie 机制在小程序开发中也有很大的需求,然而此前多数主流小程序平台并不支持 Cookie 机制,导致开发者们不得不通过小程序本地缓存的方式来模拟 Cookie 的效果。常见使用手动管理 Cookie 或者第三方库的形式来进行小程序端 Cookie 处理,但是这种方式不够完美,也会存在诸多问题。
手动管理 Cookie
使用第三方库
下面为大家详细地介绍以下这两种流行方式!
手动管理 Cookie
使用小程序数据缓存能力模拟 Cookie,只能满足基本需要,开发者负担较重。
常见的操作是,开发者封装 request 请求,从接口响应中取出需要保存的值,保存在本地缓存 storage 中,每次接口请求时,再从 storage 中读取相关数据添加进请求 header 或 body 中,以此模拟 Cookie 的效果。
// 从接口响应中取出并保存 cookie 值:
tt.request({
url: "https://xxx.com/login",
data: { /* ... */ },
success(res) {
res.header["Set-Cookie"] !== undefined && tt.setStorageSync("cookie", res.header["Set-Cookie"]);
},
fail(res) {
console.log("调用失败", res.errMsg);
},
});
// 请求的时候读缓存数据带上 cookie 信息
const header = {
'content-type': 'application/json'
};
const cookie = tt.getStorageSync("cookie");
if(url !== 'login' && cookie){
header['cookie'] = cookie;
}
tt.request({
url: "https://xxx.com/request",
data: { /* ... */ },
header,
});
- 增加开发者手动维护负担
- 需要前端开发者手动维护 Cookie;
- 需要手动区分 domain、path;
- storage 是全局存储,如果需要增加对 domain、path 作用域的支持,需要前端增加维护代码,进一步增加开发者负担;
- 要手动维护过期时间;
- 支持过期时间处理也需要前端增加维护代码,同样增加开发者负担。
2.降低小程序性能
- 如果本地缓存数据分多个变量存储,读写数据时,将出现多次 getStorageSync、 setStorageSync 调用,该方法为同步方法,需要和客户端进行数据通信,频繁调用对小程序性能有一定影响
// 连续读取
const param1 = tt.getStorageSync("param1") || "";
const param2 = tt.getStorageSync("param2") || "";
const param3 = tt.getStorageSync("param3") || "";
const param4 = tt.getStorageSync("param4") || "";
const param5 = tt.getStorageSync("param5") || "";
// 发送请求
tt.request({
data: {
param1,
param2,
// ...
}
});
b.在小程序开发工具的 trace 分析面板中可以看到这样连续调用的效果;
- 接口请求前为了设置 Cookie,需要连续多次从 storage 中同步地读取数据,getStorageSync 一次的耗时可能仅 1~3 ms,但累积起来可能达数十毫秒。特别当开发者将其封装为基础方法,在高频接口中使用时,将带来更多的性能损耗,且很容易被开发者忽视;
- Cookie 的更新类似,需要连续调用 setStorageSync 写入数据,同样带来性能损耗。
3.一些非 tt.request 请求无法处理
- 小程序中,除了 tt.request ,还有video live-player 等原生组件,以及 audio tt.previewImage 这类 API 都会发送网络请求。而这类请求受限于小程序能力开放程度,开发者无法修改其中请求参数,也就无法设置 Cookie;
// 视频请求想要标记用户这么办?
<video src="https://xxx.com/video.mp4#t=0.01#t=0.01"></video>
使用第三方库
社区中有一些第三方库支持小程序端的 Cookie 机制,使用虽然方便,但存在性能、安全性、兼容性等问题。
以 weapp-cookie 为例,通过劫持小程序的 tt.request、tt.uploadFile、tt.downloadFile 等 API ,增加自动解析和添加 Cookie 的操作,免去了开发者自行管理 Cookie 的负担。
使用第三方库相比手动管理有一定的优势:
- 只需要引入 npm 包即可使用,减轻了开发者手动维护的成本;
- 提供了 API 获取、设置 Cookie;
- 提供了 domain/path 作用域、过期等支持;
但第三方库的 Cookie 实现,最终还是基于小程序本地缓存,依然存在诸多不足:
- 同样存在频繁读取 storage 的性能问题;
- 可能与小程序的自定义 Cookie 冲突;
- 小程序 tt.request API 支持自定义设置 Cookie,但是当使用第三方库时,同名 Cookie 可能存在冲突,被第三方库覆盖掉,导致自定义 Cookie 无效。
3.无法支持 video、audio、live-player、tt.previewImage 等网络请求方式;
4.对于 uni-app、Taro 等跨平台框架可能需要第三方库进行兼容适配,否则无法使用;
5.存在安全问题,如果有恶意的第三方框架可以获取/修改 Cookie;
6.引入第三方库将增大小程序包体积。
考虑到开发者的迫切需求,以及现有使用本地缓存方式的种种弊端,抖音小程序在基础库 2.45.0 版本开始从框架层面提供了 Cookie 的支持。
抖音小程序支持服务端在 tt.request 的响应中,使用 HTTP 首部中的 Set-Cookie 字段设置 Cookie。框架侧负责 Cookie 解析、存储、匹配和发送,处理逻辑遵循 RFC6265 规范。小程序之间、小程序与宿主 Cookie 相互隔离,同时支持手动设置 Cookie。
更多介绍可查看开发者文档:小程序 Cookie 机制
小程序 Cookie 机制核心优势凸显
较市面上的两种流行方式,字节小程序官方提供的 Cookie 机制更具备以下核心优势:
功能相对丰富完善
相比开发者使用 storage 模拟实现的 Cookie,小程序 Cookie 遵循 RFC6265 规范,提供的功能比较完善和丰富:
- 支持 domain/path 作用域
- 支持 Max-Age/Expires 过期机制
- 当 Max-Age 和 Expires 同时存在时,Max-Age 优先级更高
- 兼容小程序 tt.request 的自定义 Cookie
- 兼容各类第三方框架
- 小程序 Cookie 是从平台基础能力提供的支持,即便使用 uni-app、Taro 等开发框架,只要能在小程序配置文件中开启,即可使用小程序 Cookie。
性能更优
小程序 Cookie 由框架 SDK 管理,读写速度要比前端 storage 模拟的方式快得多,根据数据来看,小程序 Cookie 的读取速度 < 1ms,写入速度在 7ms 左右。
再对比看看开发者自己读取数据的情况,性能优势相当明显:
更加安全
- 不会被前端 js 获取,防止被恶意三方框架读取或篡改
- 支持 secure 属性,保证安全传输
// Set-Cookie: "id=1; secure;"
// http 请求将不携带 Cookie
// Cookie: ''
tt.request({
url: "http:/xxx.com"
});
// https 请求才携带 Cookie
// Cookie: 'id=1'
tt.request({
url: "https:/xxx.com"
});
- 隔绝跨域设置 Cookie
// 跨域 Set-Cookie 不会被存储
url: "https://a.com/"
Set-Cookie: "id1=1; domain=b.com"
提供了一些原生组件/API中网络请求 Cookie 的支持
使用小程序 Cookie 后,原生组件/API(Video、Audio、Live-Player、tt.previewImage)也会自动携带cookie,可以解决资源请求难以标识用户问题。这在框架提供支持之前,开发者是难以做到的。
Web 应用中,一些图片、视频等资源类的请求也会使用 Cookie 来标识用户信息,以此提供更加精准和个性化的服务(如权限、个性化推荐等)。
在小程序不支持 Cookie 的时候,经常看到开发者要通过事件监听用户的操作,在事件回调中再通过额外的 tt.request 发送信息。
// ttml
<video id="id1" src="https://xxx.com/video.mp4#t=0.01#t=0.01" bindplay="playVideo"></video>
// js
playVideo(e) {
const id = e.target.id;
const userId = tt.getStorageSync('useId');
tt.request({
url: 'xxx',
data: {
userId,
videoId: id,
}
});
}
现在有了小程序 Cookie,完全不需要这么麻烦,video 组件在请求视频等资源时也能自动添加上 Cookie,服务端可以据此校验用户身份,前端不需要再写过多额外代码,还能减少 tt.request 的次数,减少网络请求,降低服务端压力。
减轻了小程序前端开发者负担
完整的 Cookie 实现是比较复杂的(参见 RFC6265),开发者们使用 storage 模拟的方式,往往也只是实现一些简单的基本功能。如果有更丰富的需求怎么办?domain/path 作用域要不要支持?过期机制要不要支持?这些都是开发者的负担。
即便封装或使用一些相关的三方库,也只是一定程度上减轻了开发者的负担,但随之又带来的性能问题、原生组件网络请求的支持问题、安全性问题、三方框架的兼容适配问题等等,依然不够令人满意。
使用小程序 Cookie 就完全不需要考虑这么多,前端开发者无需写任何代码,服务端的开发者也可以达到类似 Web 开发中的体验。
官方小程序的 Cookie 这么香,据说还有80%的人不会用.....别说我没有教过你这个“小甜饼”的正确使用方法!这届开发者已经在背着你偷偷学习了!
如果你是前端:
只需要在 app.json 中开启配置即可:
{
"cookie": {
"enableStore": true // true 开启小程序 Cookie 机制。默认 false
}
}
如果你需要发起请求时添加额外的 Cookie:
tt.request API 支持设置请求 header 的部分字段,可以添加自定义的 Cookie,小程序框架侧会将自定义 Cookie 与本地保存的 Cookie 合并,当出现冲突时,自定义 Cookie 会覆盖本地 Cookie 同名 key 部分:
// 本地 Cookie
// key1=value1; key2=value2
// 自定义 Cookie
tt.request({
url: "https://xxx.com/request",
header: {
"content-type": "application/json",
cookie: "key1=value3", // 此处添加cookie
},
success(res) {
console.log("调用成功", res.data);
},
});
// 发送出的 Cookie
// key1=value3; key2=value2
如果你是服务端:
前端配置中开启后,开发者的服务端就可以通过 HTTP 请求响应的Set-Cookie字段设置 Cookie,小程序框架会帮你保存和管理,开发者前端无需再费心处理了。
更多接入细节可查看开发者文档
看到这里,我猜你还想了解:
Q1:小程序 Cookie 如何做隔离的?
A1:不同小程序之间隔离 Cookie;宿主账号切换会清空 Cookie 数据。
Q2:Cookie 对数量、大小是否有限制?
A2:每个小程序每个域名下最多 50 个 Cookie,总 Cookie 不超过1000个,如超过限制使用 LRU 算法淘汰。每个 Cookie 大小不超过 4K,如超过则服务端此次 Set-Cookie 操作无效。
Q3:是否支持跨域 Set-Cookie?
A3:不支持。
只有真正在乎你的人,才会把“小程序 Cookie 机制”的秘密告诉你!还不快去做聪明人的选择!