Next.js + Builder.io 个性化落地页实战:从 Space 配置到 Edge 中间件定向渲染
【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples
本指南基于仓库中的 personalization-builder-io 示例,完整讲解如何用 Builder.io 为 Next.js 页面实现个性化(Personalization):包括创建 Builder.io Space、连接公私钥、配置环境变量,以及通过 Edge Middleware 重写 URL 实现按用户定向属性(targeting attributes)渲染不同页面内容。读完本文,你将掌握一套「CMS 内容 + 用户属性 + Edge 重写」的可落地个性化方案,并理解仓库中每一处关键代码背后的运行原理。
一、示例概览:它解决什么问题
传统 CMS 落地页对所有访问者返回同一份内容,无法针对不同人群(地域、设备、来源、登录状态等)展示差异化内容。本示例给出了一条完整链路:
- Builder.io负责页面内容的可视化搭建与版本管理,支持为同一 URL 创建多套「个性化变体」,每个变体绑定一组定向属性(targeting attributes);
- Next.js 页面路由
pages/[[...path]].tsx通过getStaticProps+ ISR 从 Builder 拉取页面数据; - Edge Middleware(
pages/_middleware.tsx)在边缘网络读取请求 Cookie,依据用户属性把请求重写到对应的个性化 URL(形如;/attr=value/...),从而让同一入口返回不同内容。
从源码看,仓库依赖@builder.io/personalization-utils@0.0.7、@builder.io/react@^1.1.47、js-cookie、next-seo等包(见 package.json),个性化逻辑的核心封装在personalization-utils中,业务代码保持极简。
二、获取示例:一键部署与本地克隆
原文档提供了两种使用方式。
方式一:一键部署到 Vercel
先在 Builder.io 完成环境变量配置(见下一节),然后通过 Vercel 的 Deploy 按钮直接克隆仓库并部署,部署时按提示填入BUILDER_PUBLIC_KEY与BUILDER_PRIVATE_KEY两个环境变量。
方式二:克隆并本地运行
使用create-next-app直接以本示例为模板初始化项目(npm 与 Yarn 二选一):
npx create-next-app --example https://github.com/vercel/examples/tree/main/starter/personalization-builder-io # or yarn create next-app --example https://github.com/vercel/examples/tree/main/starter/personalization-builder-io克隆完成后,项目内关键的目录结构如下(见仓库实际文件):
- pages/[[...path]].tsx:动态路由页面,负责 SSG/ISR 拉取 Builder 内容并渲染;
- pages/_middleware.tsx:Edge Middleware,执行个性化重写;
- pages/api/attributes.ts:向浏览器暴露定向属性列表的 API(供右键配置菜单使用);
- pages/_app.tsx:初始化 Builder SDK、注入用户属性与
AsyncConfigurator调试菜单; - config/builder.ts:读取
BUILDER_PUBLIC_KEY的配置入口; - components/Link/Link.tsx:将 Builder 内容里的链接映射为
next/link,保证客户端路由不刷新; - next.config.js:允许加载
cdn.builder.io图片、为可视化编辑放行 frame 嵌套。
三、配置 Builder.io Space 与连接应用
运行本项目前必须先完成两步:创建 Space、把 Space 与应用连接起来。
3.1 创建 Space
- 登录 Builder.io 账号后,首次进入会提示创建 Space:Create a new Builder site(新建站点)或Add Builder to an existing site or app(接入现有应用)。本示例应选择Add Builder to an existing site or app。
- 若没有看到引导弹窗,点击左下角Organization 图标(两个人形图案),悬停Builder.io选择+ New Space,再次选择Add Builder to an existing site or app。
- 当 Builder 询问使用的电商平台时选择None。
- 为 Space 命名(例如
My Next.js App),点击Create完成创建。
3.2 连接应用:Site URL 与 API Key
- 在 Builder.io 左侧导航栏点击Account 图标(该图标位于左侧边栏,点击后进入 Space 的关键数据页)。
- 在Space选项卡中,把Site URL改为
http://localhost:3000,并复制Public API Key。 - 回到代码编辑器,将
.env.production.example重命名为.env.production.local,并新建.env.development.local,填入公钥:
BUILDER_PUBLIC_KEY=08837cee608a405c806a3bed69acfe2d <-- 替换为你复制的 Public API Key再填入私钥:
BUILDER_PRIVATE_KEY=xxx-xxxxx <-- 替换为你的 Private API Key这里有两个要点需要强调:
- 公钥
BUILDER_PUBLIC_KEY会通过 next.config.js 的env字段暴露给浏览器端,用于渲染页面内容;它是build 时读取的,因此修改后必须重启开发服务器或重新构建。 - 私钥
BUILDER_PRIVATE_KEY只在服务端使用——它出现在 config/builder.ts 的启动校验(缺少公钥直接throw)和 pages/api/attributes.ts 中:该 API 调用getAttributes(process.env.BUILDER_PRIVATE_KEY!)拉取 Space 内定义的全部定向属性,供浏览器端右键调试菜单动态展示。私钥绝不能进入客户端代码。
四、启动应用
安装依赖并启动开发服务器:
npm install npm run dev # or yarn yarn dev生产构建与预览使用npm run build/npm start(见 package.json 的 scripts)。部署到 Vercel 时,只需在项目设置中配置相同的两个环境变量。
启动后访问http://localhost:3000,打开页面后按住 Ctrl 并右键点击页面,即可弹出个性化配置菜单——这是_app.tsx中挂载的AsyncConfigurator组件提供的,它会调用/api/attributes读取你 Space 里定义的自定义定向属性,方便在本地切换不同用户画像进行调试,这正是 README 中提示「hold ctrl + right click to show all the different personalization options」的含义。
五、核心原理拆解:个性化是怎么跑起来的
5.1 Edge Middleware:Cookie 驱动的 URL 重写
pages/_middleware.tsx 是整个个性化链路的入口。它先排除/favicon与/api前缀的请求,然后调用getPersonalizedRewrite(url.pathname, req.cookies)计算目标路径;若命中个性化规则,就把url.pathname重写为;/attr=value/...形式的路径并返回NextResponse.rewrite(url):
import { NextRequest, NextResponse } from 'next/server' import { getPersonalizedRewrite } from '@builder.io/personalization-utils' const excludededPrefixes = ['/favicon', '/api'] export default function middleware(req: NextRequest) { const url = req.nextUrl.clone() if (!excludededPrefixes.find((path) => url.pathname?.startsWith(path))) { const rewrite = getPersonalizedRewrite(url.pathname!, req.cookies) if (rewrite) { url.pathname = rewrite return NextResponse.rewrite(url) } } }这段代码的要点:
- 运行位置:Edge Middleware 在边缘网络执行,比回源到服务器再重定向更快,且对用户透明(地址栏 URL 不变)。
- 判定依据:个性化完全由 Cookie 驱动,
getPersonalizedRewrite负责把 Cookie 中的用户属性(如builder.userAttributes.city=Berlin)编码进路径。 - 排除规则:
/api被排除,避免个性化重写干扰 API 路由;/favicon被排除则是为静态资源留白。 - 无匹配行为:函数没有显式
return时,请求按原路径继续处理(隐式next()),因此普通访问不受影响。
5.2 动态路由:按 URL 形态决定拉取策略
pages/[[...path]].tsx 使用可选捕获路由[[...path]]承接所有页面路径。getStaticProps中的关键判断是:
const isPersonalizedRequest = params?.path?.[0].startsWith(';')- 普通请求(如
/、/about):以urlPath: '/' + path.join('/')作为userAttributes查询 Builder 的page模型,按 URL 匹配默认页面; - 个性化请求(路径首段以
;开头,如;/city=Berlin/about):说明请求已被 Middleware 重写,此时用getTargetingValues(path[0].split(';').slice(1))把路径中编码的键值对解析回用户属性对象,交给 Builder 查询,从而命中为该受众配置的页面变体。
const page = (await builder .get('page', { apiKey: builderConfig.apiKey, userAttributes: isPersonalizedRequest ? { ...getTargetingValues(params!.path[0].split(';').slice(1)) } : { urlPath: '/' + (params?.path?.join('/') || '') }, cachebust: true, }) .toPromise()) || nullgetStaticPaths从 Builder 拉取全部page记录(options: { noTargeting: true }表示忽略定向条件取全集),用Set去重后生成路径列表,并开启fallback: true——未被预渲染的路径在首次访问时按需生成。函数返回revalidate: 5,即 ISR 增量再生:新请求进来时最多每 5 秒重新生成一次页面,保证 Builder 后台的改动(如发布新变体)能较快生效。
5.3 渲染层:BuilderComponent + SEO + 404 兜底
组件渲染部分(同一文件的默认导出)做了三件事:
- 编辑/预览态豁免:
Builder.isEditing || Builder.isPreviewing时视为可视化编辑会话,不因内容缺失而报 404; - 404 兜底:若
page为空且处于线上状态,渲染DefaultErrorPage statusCode={404}并加noindex元信息,防止无效路径被搜索引擎收录; - 内容渲染与 SEO:用
<NextSeo>把 Builder 页面数据中的title、description、image注入标题、描述与 Open Graph 标签;<BuilderComponent renderLink={Link} model="page" content={page} />渲染 Builder 内容,并把链接组件替换为 Link.tsx 中基于next/link的实现,实现站内客户端导航。
if (router.isFallback) { return <h1>Loading...</h1> } const isLive = !Builder.isEditing && !Builder.isPreviewing if (!page && isLive) { return ( <> <Head><meta name="robots" content="noindex" /></Head> <DefaultErrorPage statusCode={404} /> </> ) }5.4 应用入口:SDK 初始化与调试配置器
_app.tsx 在应用启动时执行builder.init(builderConfig.apiKey)初始化 SDK,并通过initUserAttributes(Cookies.get())把当前 Cookie 里的用户属性注入客户端上下文,随后挂载AsyncConfigurator调试菜单(其样式来自@szhsin/react-menu)。这意味着:浏览器端也能感知并修改用户属性,配合右键菜单即可在不改代码的情况下切换不同受众预览效果。
六、在 Builder.io 后台创建个性化页面
应用跑起来后,个性化内容全部在 Builder.io 后台编排:
- 在 Builder.io 中创建一个页面(Page 模型),指定任意 URL 并发布、预览。详细的建页指引见 Builder 官方文档《Creating a landing page in Builder.io》。
- 为该页面创建多个变体,每个变体绑定一组自定义定向属性(Custom Targeting Attributes)。这些属性在后台「Targeting & Scheduling」中定义,例如
city、plan、isLoggedIn等,支持基于受众群体定向。 - 不同变体对应不同受众,发布后由本示例的 Middleware + ISR 机制自动分发:访问者带着相应 Cookie 到达时,中间件把请求重写到个性化 URL,
getStaticProps以解析出的属性为条件拉取对应变体内容并渲染。
需要再次强调的是,README 中演示用的右键调试能力依赖 pages/api/attributes.ts 返回的 Space 属性列表——该接口必须配置BUILDER_PRIVATE_KEY,否则应用会在启动时抛错(throw new Error('No BUILDER_PRIVATE_KEY defined')),这也是私钥必须正确配置的原因之一。
七、生产环境注意点
- 环境变量区分环境:公钥同时用于客户端与服务端,私钥仅服务端使用;
next.config.js中的env字段在构建时把公钥注入浏览器端,任何环境下都不能把私钥写进env暴露给前端。 - 可视化编辑的 frame 嵌套:next.config.js 为所有路径添加了
Content-Security-Policy: frame-ancestors https://*.builder.io https://builder.io响应头,允许站点被 Builder.io 以 iframe 形式嵌入做所见即所得编辑;同时images.domains放行了cdn.builder.io,保证 Builder 托管图片可被next/image正常优化加载。 - ISR 与个性化并存:
revalidate: 5让页面在保持静态化的同时周期性刷新,兼顾性能与内容更新时效;fallback: true保证任意新 URL(含个性化重写产生的新路径)都能被即时渲染。
八、小结
本示例把「CMS 内容管理」与「边缘个性化」组合成一套轻量方案:Builder.io 负责内容与受众变体编排,@builder.io/personalization-utils负责属性编解码,Edge Middleware 负责按 Cookie 重写请求,Next.js ISR 负责按需渲染。四个文件各司其职——pages/_middleware.tsx 定分发、pages/[[...path]].tsx 定渲染、pages/_app.tsx 定调试、config/builder.ts 定密钥——构成了一个可在 Vercel 上直接部署、可按受众持续迭代的个性化落地页基线。
进阶方向:在 Builder.io 后台定义更丰富的自定义定向属性(地域、设备、AB 实验分组等)以扩展受众维度;将getPersonalizedRewrite的 Cookie 策略替换为服务端会话以支持登录态个性化;结合 AB 测试(仓库中的 ab-testing-simple 示例)对个性化变体做效果度量,形成「定向 → 展示 → 度量 → 优化」的完整闭环。
【免费下载链接】examplesEnjoy our curated collection of examples and solutions. Use these patterns to build your own robust and scalable applications.项目地址: https://gitcode.com/GitHub_Trending/examples1/examples
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考