A/B 测试的本质是把不同版本的页面或功能展示给不同的用户群体,再通过数据比较哪个版本表现更好。在 Vue 3 项目中,得益于 Composition API 的响应式能力和组件化结构,实现一套完整的实验体系并不复杂,但分流怎么做才稳定、数据怎么统计才可信,这两点往往是团队容易踩坑的地方。本文将从分流策略设计、代码实现、埋点采集和结果统计四个层面,完整讲清楚 Vue 3 中的 A/B 测试落地方案。

一、分流策略设计:让同一个用户始终看到同一个版本
分流是 A/B 测试的地基。最基本的要求是一致性:同一个用户在多次访问中应该被分到同一个实验组,否则用户体验割裂,数据也会被污染。要实现这一点,必须有一个稳定的用户标识,然后再基于这个标识做确定性分组。
最简单的方案是纯随机分组:用户首次进入页面时生成一个随机数,按概率分到 A 组或 B 组,并把分组结果写入 localStorage。这种方案实现成本最低,适合中小型项目。但它有一个明显缺陷——分组结果和用户身份无关,一旦用户清理浏览器缓存或者换设备,就可能被分到另一个组,造成数据抖动。
更稳妥的做法是哈希分桶。取用户的唯一标识(如登录后的 userId,或匿名场景下的设备 ID),拼接实验名称,计算一次哈希值,再对总桶数取模。由于哈希函数是确定性的,同一个用户在任何时间、任何端计算出的分组都一致,无需依赖本地存储。业界常用的分桶数是 100 或 1000,方便表达任意比例的灰度放量,比如前 5 个桶给实验组,就代表 5% 的流量。
如果是登录态产品,还可以采用服务端下发实验标记的方案:客户端在请求初始化数据时,服务端根据用户 ID 完成分流,把实验分组信息随接口返回。这种方案分流逻辑收敛在服务端,跨端一致性最好,也是中大型团队的主流选择。缺点是需要后端配合,前端失去了独立开实验的灵活性。
二、Vue 3 代码实现:Composable 封装实验能力
在 Vue 3 中,推荐把分流逻辑封装成一个 Composable,供任意组件调用。下面是一个基于哈希分桶的实现示例:
import { ref } from 'vue'
// 简单的字符串哈希函数(djb2 算法)
function hashString(str) {
let hash = 5381
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i)
}
return Math.abs(hash)
}
// 获取用户标识,登录用户优先,匿名用户用本地生成的设备 ID
function getUserId() {
const loginId = localStorage.getItem('userId')
if (loginId) return loginId
let anonId = localStorage.getItem('anonId')
if (!anonId) {
anonId = 'anon-' + Math.random().toString(36).slice(2, 11)
localStorage.setItem('anonId', anonId)
}
return anonId
}
export function useABTest(experimentName, variants, rolloutRatio = 1) {
const TOTAL_BUCKETS = 100
const bucket = hashString(getUserId() + ':' + experimentName) % TOTAL_BUCKETS
// 只放量 rolloutRatio 比例的流量参与实验,其余用户走默认版本
const inExperiment = bucket < TOTAL_BUCKETS * rolloutRatio
const variant = inExperiment
? variants[bucket % variants.length]
: 'default'
const currentVariant = ref(variant)
// 埋点上报:记录用户进入实验的事实
reportExposure(experimentName, variant, inExperiment)
return { variant: currentVariant }
}在组件中使用时,直接根据 variant 的值条件渲染不同的界面版本:
<template>
<div class="button-ab">
<button v-if="variant === 'red'" class="btn-red">立即购买</button>
<button v-else class="btn-blue">立即购买</button>
</div>
</template>
<script setup>
import { useABTest } from '@/composables/useABTest'
// red 组占 50% 流量,blue 组占 50% 流量
const { variant } = useABTest('buy-button-color', ['red', 'blue'])
</script>
</script>有几个细节值得注意。第一,reportExposure 曝光埋点必须在用户真实看到实验版本时才上报,如果实验组件在首屏之外、需要滚动才可见,应该用 IntersectionObserver 延迟上报,否则会把没看到实验的用户也算进样本,拉低统计精度。第二,哈希函数选型上,djb2 足够简单且分布均匀,如果对安全性有要求可以换成 SHA-256 的前几位再取模。第三,实验名称一定要带版本号后缀(如 buy-button-color-v2),避免迭代实验时新旧数据混在一起。
三、埋点采集:定义好指标才有可信数据
分流解决的是"给谁看什么"的问题,埋点解决的则是"看完之后发生了什么"。开实验之前,必须先明确核心指标。以按钮改色实验为例,核心指标通常是点击率,辅助指标包括下单转化率、页面停留时长等,同时还要监控加噪指标(如报错率、页面加载时间),防止新版本引入性能问题。
埋点事件的设计要保持简单和统一。最少需要两类事件:曝光事件(用户看到了某实验的某版本)和转化事件(用户完成了目标行为)。上报时携带统一的字段结构:
// 曝光事件:每个用户每场实验只上报一次
{
event: 'ab_exposure',
experiment: 'buy-button-color-v2',
variant: 'red',
userId: 'u_10086',
timestamp: 1710000000000
}
// 转化事件:用户完成目标行为时上报
{
event: 'buy_button_click',
experiment: 'buy-button-color-v2', // 冗余携带实验信息,方便后续归因
variant: 'red',
userId: 'u_10086',
timestamp: 1710000005000
}转化事件里冗余携带实验名和版本号是一个实用技巧。实际生产环境中,埋点链路可能出现丢失或乱序,如果转化事件自身携带分组信息,统计时就不需要再去关联曝光表,容错性大幅提升。另外,曝光事件要做去重(同一用户重复上报要合并),否则分母被放大,算出来的转化率会系统性偏低。
四、结果统计:样本量与显著性检验
实验跑完之后,最容易犯的错误是看一眼"红组点击率 3.2%、蓝组 3.0%"就直接宣布红组获胜。样本量不足时,这种差异很可能只是随机波动。正确的做法分两步:先估算所需样本量,再做统计显著性检验。
样本量估算的核心逻辑是:基准转化率越低、想检测的差异越小,需要的样本量就越大。经验公式为:每组样本量 ≈ 16 × p(1-p) / δ²,其中 p 是基准转化率,δ 是预期的最小提升幅度。举例来说,基准点击率 3%,期望提升 10%(即从 3% 提升到 3.3%),δ = 0.003,代入可得每组约需 51500 个样本。如果日均流量只有几千,这场实验需要跑半个多月才能得出结论,提前中止只会得到噪音。
判断显著性最常用的是双比例 Z 检验。假设两组样本量和转化数如下,可以直接计算:
// 双比例 Z 检验
// A 组:10000 曝光,320 点击;B 组:10000 曝光,365 点击
function abTest(n1, x1, n2, x2) {
const p1 = x1 / n1
const p2 = x2 / n2
// 合并比例
const p = (x1 + x2) / (n1 + n2)
const z = (p2 - p1) / Math.sqrt(p * (1 - p) * (1 / n1 + 1 / n2))
// 正态分布下 |z| > 1.96 对应 95% 置信水平
const significant = Math.abs(z) > 1.96
return { p1, p2, z, significant }
}
console.log(abTest(10000, 320, 10000, 365))
// z ≈ 1.80,未达到 95% 置信水平,差异不显著上面这个例子里 z 值约为 1.80,小于 1.96,说明 3.2% 和 3.65% 的差异还不足以证明 B 版本更好,应该继续积累样本。只有当 z 值超过阈值时,才有底气说"这个差异在 95% 的置信水平下不是偶然"。如果团队对统计严谨性要求更高,可以进一步采用贝叶斯方法估计"某版本更优的概率",或者引入 CUPED 等方差缩减技术来降低所需样本量,这些方案在成熟的实验平台中都有成熟实现。
最后提醒一点:实验结论要防"偷看效应"。不要每天看一次数据、一旦显著就立刻停实验,频繁检验会大幅提高假阳性率。规范的做法是预先确定实验时长或最小样本量,达到条件后再做一次性判断。把分流、埋点、统计这三件事都做扎实,Vue 3 项目里的 A/B 测试才能真正为产品决策提供可靠依据。