跳转到主要内容
Subotiz A/B 实验前端 SDK。在浏览器中拉取实验分组数据、支持按需上报转化事件。
  • 浏览器优先,不依赖任何 UI 框架(React / Vue / 原生页面均可)
  • 原生 TypeScript 支持,类型完整

通过 CDN 加载

可通过 CDN 引入 SDK。 CDN 模式调用语法:abSubotiz(方法名, ...参数)

上报事件

业务事件通过 track(eventName, properties?) 上报,常用于实验转化分析(注册完成、按钮点击、自有收入 / LTV 等平台无法自动采集的指标):
特性:
  • 永远上报、永不丢弃——SDK 不在客户端做白名单拦截,任意 eventName 都会发往后端。是否计入某个实验由后端归因,无需前端关心
  • 永远成功返回,不抛出异常
  • 网络失败 / 5xx → 入 localStorage 离线队列,回到在线状态时自动重发
  • 4xx → 静默丢弃(事件本身不合法)
  • init 未完成时调用 → 静默跳过
不要在事件参数中传用户隐私数据(手机号、邮箱、身份证、地址等)。SDK 不做过滤,由调用方自行约束。

数值指标:properties.value

需要求和 / 平均值类聚合的指标(如收入、LTV、停留时长),把数值放在 properties.value 上:
  • value 必须是有限数字typeof === 'number' 且非 NaN / Infinity)才会被后端纳入数值聚合。
  • 非法的 value(字符串 '99.9'NaNnull 等)不会污染数值统计,会作为普通标签原样透传,由后端按”非数值”降级处理。
  • 计数 / 去重类指标(如 signup_complete无需value,直接 track('signup_complete') 即可。
  • properties 中除 value 外的其他字段都作为附加标签随事件上报,字段名自定。

调试:指标白名单提示

后台为实验声明的指标 key 会随分桶结果下发,SDK 在 debug: true 时据此做拼写提示:当 trackeventName 不在任何运行中实验声明的指标里,控制台会 warn(提示可能拼写错误 / 大小写不符,并打印当前白名单)。 只是调试提示——事件依然正常上报。生产模式(debug: false)下完全无此开销,不影响上报行为。

曝光事件

实验首次命中时 SDK 会自动上报曝光事件,不需要手动调用。粘性缓存中已存在的实验不重复上报。

Pricing 链接自动注入

实验对象类型为 price_list 时,SDK 会在返回的价格表链接中,追加以下 query 参数。业务方拿到的链接已经是注入完毕的:

来源渠道定向 (sourceChannel)

init 时可传入 sourceChannel,用于让后端按流量来源做实验定向分流(例如”仅对来自 facebook 的流量开启某实验”)。其余定向维度(设备、语言、国家、IP)由后端从 HTTP 请求头自动解析,SDK 不采集任何浏览器指纹 / UA / 地理位置信息
传入后,SDK 会在两处携带该值:
  1. 分桶请求:写入 POST /sdk/v1/ab/assign 请求体的 attributes.source_channel,供后端定向规则匹配。
  2. Pricing 链接:对 price_list 类型实验的链接追加 ab_sbt_sch query 参数(与上面的值同源)。

取值与归一化规则

SDK 不做枚举校验、不做大小写转换,取值口径(如 facebook / google / tiktok)由您与产品侧自行约定。携带前仅做如下处理:
稳定性sourceChannel 在单次会话内被视为稳定值,仅 init 时确定一次。后续不监听变化、不会因渠道变化重新拉取分桶。未携带优于截断:超长 / 非法值一律不携带,避免后端拿到被静默改写的值后与您的埋点配置产生偏差。

API 参考

init(config)

初始化 SDK。在每个 SDK 实例上只能调用一次,重复调用会被忽略。立即返回,不阻塞主线程。
  • 参数 config: SubotizSDKConfig
  • 返回 void

配置项

init(config) 接受一个 SubotizSDKConfig 对象:

onReady(onSuccess, onError?)

注册就绪回调。SDK 已就绪时同步触发,未就绪时异步触发。可重复注册多个。
  • 参数
    • onSuccess: (experiments: SubotizExperimentMap) => void
    • onError?: (error: ABTestError) => void
  • 返回 void

waitReady()

onReady 的 Promise 版本。
  • 返回 Promise<SubotizExperimentMap>
  • 失败时:抛出 ABTestError(与 onReadyonError 等价)

isReady()

同步检查 SDK 是否已就绪。
  • 返回 boolean

getExperiments()

同步获取当前所有命中实验。
  • 返回 SubotizExperimentMap | null
    • null —— 未初始化、未就绪、或当前没有任何命中实验
    • Record<string, SubotizExperiment> —— 实验映射表,键为 experimentId

track(eventName, properties?)

上报自定义业务事件。任意事件都会上报,是否计入实验由后端归因。
  • 参数
    • eventName: string —— 事件名
    • properties?: Record<string, unknown> —— 事件附加属性。其中 value(有限数字)用于求和 / 均值类指标;其余字段作为附加标签透传
  • 返回 void(永不抛错)
  • 行为
    • 永远上报,不在客户端做白名单丢弃
    • debug: trueeventName 不在后台声明的指标白名单中 → 控制台 warn 拼写提示(仅提示,不影响上报)
    • init 未完成时调用 → 静默跳过

forceVariant(experimentId, variantKey)

debug: true 时生效。本地强制覆盖某实验的变体,便于开发期验证不同变体的 UI。
  • 参数 experimentId: stringvariantKey: string
  • 返回 void

错误处理

onReady(_, onError)waitReady() 失败时返回 ABTestError
错误码:

降级策略

SDK 内置降级策略,业务方只需关心三件事:
  1. 首次渲染走对照组 —— await waitReady() 在网络异常时最长会等 timeout 秒,不要用它阻塞首屏
  2. onReady / waitReady 中切换变体 —— 用户视觉冲击最小
  3. onError 时保持对照组 —— 不展示空白或异常 UI

调试

打开 debug: true 后,SDK 会在控制台输出请求与队列状态:

重置 SDK 状态

本地存储键

SDK 在 localStorage 中使用以下键:

常见问题

SDK 加载之前业务代码已经渲染了,会拿不到实验数据?

会。让业务代码先渲染对照组,再在 onReady / waitReady 中切换到变体。粘性缓存保证用户刷新后稳定看到自己的变体(首次访问会有”对照组 → 变体”的瞬时切换)。

track 调用了,后端没收到怎么排查?

按顺序检查:
  1. abSubotiz.isReady() 是否为 true?未就绪时 track 静默跳过。
  2. 打开 debug: true,看控制台是否有 track: sending 日志。
  3. 看浏览器网络面板有没有 POST /sdk/v1/ab/events 请求。
  4. 响应是 4xx 还是 5xx?4xx 会被丢弃,5xx 会入队等待重发。
  5. localStorage.ab_event_queue_*_track 是否积压事件。

我传了 sourceChannel,但实验定向没生效

按顺序检查:
  1. 打开 debug: true,看分桶请求体 attributes.source_channel 是否带上了您的值。
  2. 确认值合法:trim 后非空、长度 < 50。超长会被忽略(控制台有 warn)。
  3. 确认值与后台定向规则完全一致——SDK 不做大小写转换,Facebookfacebook
  4. 定向规则匹配在后端完成;命中与否可用 getExperiments() 看是否拿到该实验。

trackvalue 没有进入求和 / 均值统计

value 必须是有限数字typeof === 'number' 且非 NaN / Infinity)才会被纳入数值聚合。常见错误是传入字符串:track('ltv', { value: '99.9' })(字符串)会被当成普通标签,不参与 sum / average,应改为 { value: 99.9 }

同一个实验,刷新前后变体不一致

SDK 粘性缓存保证 60 天内变体稳定。出现不一致通常是:
  • localStorage 被清除(隐私模式 / 用户主动清空)
  • 后台开启了用户强制分配,会覆盖粘性缓存

实验在生产没生效,怎么排查

按顺序检查:
  1. storeId 是否正确(错误的 storeId 不报错,只是拿不到任何实验)
  2. 后台实验是否处于”运行中”状态(暂停 / 草稿状态不会下发)
  3. 用户是否命中实验定向规则(设备 / 国家由后端从 HTTP 请求头解析)
  4. getExperiments() 看返回结果,确认是不是变体决策本身就是对照组

我想让某个用户完全不参与实验

SDK 不提供”退出实验”接口。变通方案:业务代码自行判断(如登录态 / 角色),命中条件时不读 experiments 直接走默认渲染。

getExperiments() 为什么返回 null 而不是空对象

null 表示”未就绪”或”无任何命中实验”,便于做存在性判断:
避免写 Object.keys(all).length === 0

React 服务端组件 / Edge Runtime 能用吗

不能。SDK 依赖 localStorage / document.cookie / window 等浏览器接口。

浏览器兼容性

依赖原生 fetch / Promise / localStorage。支持:
  • Chrome / Edge ≥ 最近 2 个大版本
  • Safari ≥ 最近 2 个大版本
  • Firefox ≥ 最近 2 个大版本
不支持 IE。SDK 不内置任何兼容垫片。