如何在 Astro 中设置 A/B 测试(译)
A/B 测试可以帮助你通过比较变更对关键指标的影响来改进 Astro 应用。为了展示如何设置 A/B 测试,我们将创建一个基本的 Astro 应用,添加 PostHog,创建一个 A/B 测试,并实现其代码。
整体实现思路
-
环境准备
- 安装 Node.js(v18+)并初始化 Astro 项目
- 创建基础页面结构(标题 + 按钮交互)
-
集成分析工具
- 通过 PostHog Web 片段注入 SDK
- 创建布局组件统一管理监测代码
- 验证事件采集功能
-
定义实验指标
- 捕获按钮点击自定义事件
- 配置事件属性关联用户行为
-
创建 A/B 测试实验分组
- 在 PostHog 配置多版本实验参数
- 设置核心转化指标(按钮点击率)
-
实验逻辑实现
- 客户端渲染:实时获取功能标志但存在 UI 闪烁
- 服务端渲染:通过 Node SDK 预取标志,提升首屏稳定性
分组实现
客户端渲染实现
// src/components/posthog.astro
loaded: (posthog) => {
posthog.onFeatureFlags(() => {
const button = document.querySelector('.main-button');
const variant = posthog.getFeatureFlag('my-cool-experiment');
button.innerText = {
control: '控制变体',
test: '测试变体'
}[variant] || '默认文案';
});
}
实现步骤:
- 在 PostHog 加载完成后注册功能标志监听
- 通过 CSS 选择器获取按钮 DOM 元素
- 根据返回的 variant 值动态更新按钮文案
- 自动处理标志未生效时的默认状态
服务端渲染实现
// src/posthog-node.js
export default function PostHogNode() {
if (!posthogClient) {
posthogClient = new PostHog('<ph_key>', {
host: 'https://us.i.posthog.com',
fetch: (url, options) => fetch(url, { ...options, next: { revalipubDate: 60 } })
});
}
return posthogClient;
}
// pages/index.astro
const distinctId = ctx.cookies.get('distinct_id') || crypto.randomUUID();
const variant = await PostHogNode().getFeatureFlag('my-cool-experiment', distinctId);
核心配置:
- 创建 Node.js 客户端实例并配置缓存策略
- 通过 cookies 或随机生成稳定 distinctId
- 服务端预取功能标志状态
- 同步设置客户端 distinctId 保持一致性
方案对比
维度 | 客户端渲染 | 服务端渲染 |
---|---|---|
首屏加载 | 存在 UI 闪烁 | 无内容抖动 |
数据时效性 | 实时更新标志状态 | 需要配置缓存刷新策略 |
实现复杂度 | 仅需前端逻辑 | 需要 Node 环境支持 |
SEO 友好性 | 客户端状态不影响爬虫 | 服务端直出完整内容 |
用户 ID 管理 | 依赖浏览器指纹 | 可定制 ID 生成策略 |
最佳实践建议:
- 营销类实验优先采用客户端方案快速上线
- 关键功能改版推荐服务端方案保证稳定性
- 结合
getFeatureFlag
+onFeatureFlags
实现混合渲染
1. 创建 Astro 应用
首先,确保已安装 Node.js(版本 18.0 或更新)。然后,创建一个新的 Astro 应用:
终端
npm create astro@latest
在命令行中提示时,命名你的新项目目录(我们选择 astro-ab-test
),选择 Empty
作为新项目,选择 No
使用 TypeScript,安装依赖项,并选择 No
创建 git 仓库。
接下来,将 src/pages/index.astro
中的代码替换为一个简单的标题和按钮:
index.astro
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro A/B 测试</h1>
<button class="main-button">点击我!</button>
</body>
</html>
运行 npm run dev
并导航到 http://localhost:4321 查看你的应用。
2. 在应用中添加 PostHog
应用设置完成后,是时候安装并设置 PostHog 了。如果你没有 PostHog 实例,可以 免费注册。
完成后,回到你的 Astro 项目,在 src
文件夹中创建一个新的 components
文件夹。在这个文件夹中,创建一个 posthog.astro
文件
终端
cd ./src
mkdir components
cd ./components
touch posthog.astro
在这个文件中,添加你的 Web 片段
,你可以在 项目设置 中找到。
posthog.astro
---
---
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init(
'<ph_project_api_key>',
{
api_host:'https://us.i.posthog.com',
}
)
</script>
下一步是创建一个 布局,我们将在其中使用 posthog.astro
。在 src
中创建一个新的 layouts
文件夹,然后创建一个新文件 Layout.astro
:
终端
cd .. && cd .. # 如果你仍在 src/components/posthog.astro,则返回基础目录
cd ./src
mkdir layouts
cd ./layouts
touch Layout.astro
将以下代码添加到 Layout.astro
:
Layout.astro
---
import PostHog from '../components/posthog.astro'
---
<head>
<PostHog />
</head>
最后,更新 index.astro
以使用新的布局:
index.astro
---
import Layout from '../layouts/Layout.astro';
---
<Layout>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro A/B 测试</h1>
<button class="main-button">点击我!</button>
</body>
</html>
</Layout>
完成后,重新加载你的应用并点击按钮几次。你应该会在 PostHog 事件探索器 中看到事件。
3. 捕获自定义事件
在 PostHog 中设置 A/B 测试的第一步是设置目标指标。我们将使用按钮的点击次数作为目标。
为了测量这一点,我们在按钮被点击时 捕获自定义事件 home_button_clicked
。为此,更新 posthog.astro
中的代码,添加一个 <script>
并在按钮被点击时调用 posthog.capture()
。
index.astro
---
import Layout from '../layouts/Layout.astro';
---
<Layout>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro A/B 测试</h1>
<button class="main-button">点击我!</button>
<script>
const button = document.querySelector('.main-button');
button.addEventListener('click', () => {
window.posthog.capture('home_button_clicked')
});
</script>
</body>
</html>
</Layout>
设置完成后,刷新你的应用并点击按钮几次,以在 PostHog 中看到捕获的事件。
4. 在 PostHog 中创建 A/B 测试
接下来,转到 A/B 测试选项卡 并通过点击 新建实验 按钮创建一个 A/B 测试。为你的实验添加以下详细信息:
- 命名为 “我的酷实验”。
- 设置 “功能标志键” 为
my-cool-experiment
。 - 对所有其他字段使用默认值。
- 点击 保存为草稿。
创建完成后,将主要指标设置为 home_button_clicked
的趋势,然后点击 启动。
5. 实现 A/B 测试代码
在实现我们的实验代码时,有两个选项:
- 客户端渲染
- 服务器端渲染
我们将展示如何实现这两种方式。
客户端渲染
为了实现 A/B 测试,我们使用 posthog.onFeatureFlags
回调来根据用户是否在实验的 control
或 test
变体中更新按钮文本。
更新 /components/posthog.astro
中的代码,在 PostHog 的 loaded
回调中实现 posthog.onFeatureFlags
代码:
posthog.astro
---
---
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init(
'<ph_project_api_key>',
{
api_host:'https://us.i.posthog.com',
loaded: (posthog) => {
posthog.onFeatureFlags(() => {
const button = document.querySelector('.main-button');
if (posthog.getFeatureFlag('my-cool-experiment') === 'control') {
button.innerText = '控制变体';
} else if (posthog.getFeatureFlag('my-cool-experiment') === 'test') {
button.innerText = '测试变体';
}
});
}
}
)
</script>
现在如果你刷新你的应用,你应该会看到按钮文本更新为 控制变体
或 测试变体
。用户会自动分配到两者之一,PostHog 继续跟踪按钮点击,你可以在 PostHog 中查看 A/B 测试的结果。
服务器端渲染
注意,当你刷新页面时,按钮文本会在 点击我!
和 控制/测试变体
之间闪烁。这是因为 PostHog 加载并发出功能标志请求需要时间。
服务器端渲染是一种避免这种情况的方法。这种方式在客户端页面加载之前获取功能标志。
要设置这个,我们必须安装并使用 PostHog 的 Node 库(因为我们正在发出服务器端请求)。
终端
npm install posthog-node
在 src
文件夹中,创建一个 posthog-node.js
文件。这是我们设置代码以创建 PostHog Node 客户端的地方。你可以在 项目设置 中找到你的 API 密钥和实例地址。
src/posthog-node.js
import { PostHog } from 'posthog-node';
let posthogClient = null;
export default function PostHogNode() {
if (!posthogClient) {
posthogClient = new PostHog('<ph_project_api_key>', {
host: 'https://us.i.posthog.com',
});
}
return posthogClient;
}
接下来,我们将 posthog-node.js
导入 pages/index.astro
。然后我们使用它来获取功能标志并更新按钮文本:
index.astro
---
import Layout from '../layouts/Layout.astro';
import PostHogNode from '../posthog-node.js';
let buttonText = '无变体'
try {
const distinctId = 'placeholder-user-id'
const enabledVariant = await PostHogNode().getFeatureFlag('my-cool-experiment', distinctId);
if (enabledVariant === 'control') {
buttonText = '控制变体';
} else if (enabledVariant === 'test') {
buttonText = '测试变体';
}
} catch (error) {
buttonText = '错误';
}
---
<Layout>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro A/B 测试</h1>
<button class="main-button">{buttonText}</button>
<script>
const button = document.querySelector('.main-button');
button.addEventListener('click', () => {
window.posthog.capture('home_button_clicked')
});
</script>
</body>
</html>
</Layout>
最后,你可以移除我们在 posthog.astro
中添加的用于客户端渲染功能标志的代码:
posthog.astro
---
---
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n