资讯详情

Build-Time Targeting 示例:基于自定义特征的静态变体生成与边缘渲染

📅 2026/10/8 19:17:17 | 华诺云谱 👁 阅读
Build-Time Targeting 示例:基于自定义特征的静态变体生成与边缘渲染
低代码前端后端【免费下载链接】plasmicVisual builder for React. Build apps, websites, and content. Integrate with your codebase.项目地址https://gitcode.com/gh_mirrors/pl/plasmic点击查看免费下载本示例基于 Next.js Pages Router 与plasmicapp/loader-nextjs演示如何将 UTM 来源、浏览器类型等自定义特征custom traits编码进 URL 路径在构建期build time生成全部可能的变体页面并通过 Edge Middleware 将请求重写到对应的静态变体。文章会同时对照仓库中同类的 custom-targeting 示例与 loader-edge 源码帮助你理解这一模式从路径编码到边缘重写的完整链路。一、示例概览与目标效果仓库中的 build-time-targeting 是官方提供的「构建期目标定位」参考工程。它与运行时动态渲染变体不同采用构建期穷举 边缘重写策略构建期利用generateAllPathsWithTraits枚举出所有可能的特征组合路径生成对应静态页面请求期Edge Middleware 读取请求特征utm_source、browser用getMiddlewareResponse计算出目标路径并重写使不同特征的访问者落到不同静态变体上。示例定义的命中规则非常简单browserchromeutm_sourcegoogle验证方式访问https://build-time-targeting.vercel.app/?utm_sourcegoogle会展示utm_source google的变体使用不同浏览器访问会展示browser chrome的变体。工程根目录结构如下build-time-targeting/ ├── plasmic-init.ts # 初始化 Loader 并注册自定义特征 ├── middleware.ts # Edge Middleware解析特征并重写路径 ├── next.config.js # Next.js 配置 ├── package.json # 依赖与脚本 └── pages/ ├── [[...catchall]].tsx # 全量捕获页静态路径生成 SSR 变体提取 ├── plasmic-host.tsx # App Host 画布宿主页 └── api/hello.ts阅读本示例时可以对照同仓库的 custom-targeting运行时动态变体与 custom-targeting-codegen代码生成模式来理解不同变体方案的差异。二、注册自定义特征plasmic-init.ts自定义特征custom traits是决定页面变体的输入维度需要在 Loader 初始化时注册。打开 plasmic-init.tsimport { initPlasmicLoader } from plasmicapp/loader-nextjs; export const PLASMIC initPlasmicLoader({ projects: [ { id: qSU617xDVJeD8V18Bsr4AA, token: 9cZbD1xYkMbOkRInGNShESoR5f8hD5YU6HstGlYDEiFsZpVmxG4WsGPLTIZA9KDTS3jYSRjYAwrIZfxSdE6ow, }, ], // 默认使用项目最后一次发布published的版本 // 开发阶段可设为 true 使用未发布版本但性能显著更慢 preview: false, });接着注册两个自定义特征PLASMIC.registerTrait(browser, { type: choice, label: Browser, options: [Chrome, Safari, Other], }); PLASMIC.registerTrait(utm_source, { type: text, label: UTM Source, });要点说明registerTrait由 Loader 提供用于声明特征的类型与取值空间。type为choice时需给出options枚举为text时则接受任意字符串这里注册的browser与utm_source是自定义特征与之相对的是 Plasmic 内置的pageUrl等特征见 variation.ts 中getActiveVariation对pageUrl的注入同一个特征会被三个环节使用Edge Middleware 读取请求值、getActiveVariation在 SSR 时挑选变体、generateAllPathsWithTraits在构建期穷举路径。对应地页面需要提供 Plasmic 画布宿主。参见 pages/plasmic-host.tsximport { PLASMIC } from /plasmic-init; import { PlasmicCanvasHost } from plasmicapp/loader-nextjs; export default function PlasmicHost() { return PLASMIC PlasmicCanvasHost /; }该页面用于在 Plasmic Studio 中将本项目设置为 App Host从而在设计器中实时预览代码组件与变体。三、请求期特征解析与路径重写middleware.tsEdge Middleware 是本模式的核心枢纽它负责读取请求特征 → 计算目标路径 → 重写响应。import { getMiddlewareResponse } from plasmicapp/loader-nextjs/edge; import { NextRequest, NextResponse, userAgent } from next/server; // 排除确定不是 Plasmic 变体页面的路径 export const config { matcher: [/:path((?!_next/|api/|favicon\\.ico|plasmic-host).*)], }; export async function middleware(req: NextRequest) { // 只为 GET 请求挑选动态变体 if (req.method ! GET) { return; } // Next.js 基于 ua-parser-js 解析 User-Agent const ua userAgent(req); const browser ua.browser.name?.includes(Chrome) ? Chrome : ua.browser.name?.includes(Safari) ? Safari : Other; const newUrl req.nextUrl.clone(); const PLASMIC_SEED req.cookies.get(plasmic_seed); // 将请求重写到编码了自定义特征以及 A/B 测试随机种子的新路径 const { pathname, cookies } getMiddlewareResponse({ path: newUrl.pathname, traits: { // 为正在使用的自定义特征提供取值通常来自 cookie 或查询参数 ...(req.nextUrl.searchParams.get(utm_source) ? { utm_source: req.nextUrl.searchParams.get(utm_source) ?? } : {}), browser, }, cookies: { ...(PLASMIC_SEED ? { plasmic_seed: PLASMIC_SEED.value } : {}), }, }); // 用新 pathname 重写响应 newUrl.pathname pathname; const res NextResponse.rewrite(newUrl); // 保存需要写入 cookie 的内容——即随机种子对应的自定义特征。 // 每次访问使用同一个随机种子挑选 A/B 测试桶 // 确保同一访问者始终看到同一个 A/B 测试桶。 cookies.forEach((cookie) { res.cookies.set(cookie.key, cookie.value); }); return res; }关键点拆解1. matcher 排除非变体路径。_next/静态资源、api/API 路由、favicon.ico、plasmic-host画布宿主都不会经过本中间件避免不必要的重写开销。2. 特征读取策略的差异。browser由userAgent(req)解析而来归类为Chrome/Safari/Otherutm_source则从查询参数读取。值得对比的是仓库中 custom-targeting 的写法它直接无条件读取utm_sourcereq.nextUrl.searchParams.get(utm_source) ?? 而本示例用展开运算符做条件判断无该参数时不携带该特征。两种写法的取舍在于缺省时是回退到默认变体还是作为特征参与变体匹配。3.getMiddlewareResponse的作用。它返回{ pathname, cookies }pathname是把特征按固定格式编码进 URL 的结果cookies是在访问者尚无plasmic_seedcookie 时新生成的种子用于 A/B 测试分桶。从 loader-edge 源码 可以确认其内部逻辑export const getMiddlewareResponse (opts: { path: string; traits: Traits; cookies: Recordstring, string; seedRange?: number; }) { const newCookies: { key: string; value: string }[] []; const seedRange Number.isInteger(opts.seedRange) ? opts.seedRange : DEFAULT_PLASMIC_SEED_RANGE; // 默认 16 const seed opts.cookies[PLASMIC_SEED] || getSeed(seedRange); let traits opts.traits; if (seedRange seedRange 0) { traits { ...traits, [PLASMIC_SEED]: seed }; if (!opts.cookies[PLASMIC_SEED]) { newCookies.push({ key: PLASMIC_SEED, value: seed }); } } return { pathname: rewriteWithTraits(opts.path, traits), cookies: newCookies, }; };也就是说如果访问者 cookie 中已有plasmic_seed则复用否则随机生成 0~15 之间的种子并写入 cookie。这保证了同一访问者跨多次请求始终落在同一个 A/B 测试桶。4. 特征编码格式。rewriteWithTraits把特征编码为__pm__keyvalue形式拼在路径尾部见 variation.tsexport const rewriteWithTraits (path: string, traits: Traits) { if (Object.keys(traits).length 0) { return path; } return ${path}${path.endsWith(/) ? : /}${expandTraits(traits)}; };而expandTraits会把多个特征按键名排序后逐一拼接。对应的解码逻辑是rewriteWithoutTraitsvariation.ts在服务端解析路径时把特征剥离出来。四、构建期路径生成与变体提取pages/[[...catchall]].tsx全量捕获页是变体方案的消费端它承担三个职责构建期生成全部变体路径、SSR 时解析当前变体、渲染对应页面。import { ComponentRenderData, extractPlasmicQueryData, PlasmicComponent, PlasmicRootProvider, } from plasmicapp/loader-nextjs; import type { GetStaticPaths, GetStaticProps } from next; import { PLASMIC } from /plasmic-init; import { generateAllPathsWithTraits, getActiveVariation, rewriteWithoutTraits, } from plasmicapp/loader-nextjs/edge;4.1 构建期getStaticPaths穷举所有变体路径export const getStaticPaths: GetStaticPaths async () { const pageModules await PLASMIC.fetchPages(); function* gen() { for (const page of pageModules) { // 为当前页面生成包含全部变体的所有可能路径 const allPaths generateAllPathsWithTraits(page.path, { browser: [Chrome, Safari, Other], utm_source: [google, facebook], }); for (const path of allPaths) { yield { params: { catchall: path.substring(1).split(/), }, }; } } } return { paths: Array.from(gen()), fallback: false, }; };注意generateAllPathsWithTraits的第二参数传入了每个特征的全部取值browserChrome、Safari、Other与 plasmic-init.ts 中注册的options一致utm_sourcegoogle、facebook。从 loader-edge 源码 看其穷举逻辑默认会生成 16 个随机种子0~15对应的plasmic_seed组合再与各特征的取值做笛卡尔积最终对每种组合调用rewriteWithTraits编码成路径。因此路径总数 特征组合数 × 16种子数。fallback: false表示只允许已枚举的路径命中该页面这保证了构建产物完全可预测但也意味着所有特征取值必须在构建期已知——这正是「构建期目标定位」与运行时方案的根本区别。4.2 SSR 期getStaticProps解析变体并预取数据export const getStaticProps: GetStaticProps async (context) { const { catchall } context.params ?? {}; const rawPlasmicPath typeof catchall string ? catchall : Array.isArray(catchall) ? /${catchall.join(/)} : /; // 解析路径并剥离出特征 const { path: plasmicPath, traits } rewriteWithoutTraits(rawPlasmicPath); const plasmicData await PLASMIC.maybeFetchComponentData(plasmicPath); if (!plasmicData) { // 非 Plasmic 捕获页 return { props: {} }; } // 获取当前页面的活跃变体 const variation getActiveVariation({ splits: PLASMIC.getActiveSplits(), traits, path: plasmicPath, }); const pageMeta plasmicData.entryCompMetas[0]; // 缓存页面所需的数据 const queryCache await extractPlasmicQueryData( PlasmicRootProvider loader{PLASMIC} prefetchedData{plasmicData} pageParams{pageMeta.params} variation{variation} PlasmicComponent component{pageMeta.displayName} / /PlasmicRootProvider ); // 如需增量静态再生成ISR可设置 revalidate return { props: { plasmicData, queryCache, variation }, revalidate: 60 }; };流程拆解rewriteWithoutTraits把路径中的__pm__特征段解析为{ path, traits }对应 variation.tsPLASMIC.maybeFetchComponentData(plasmicPath)拉取该路径对应的组件渲染数据getActiveVariation结合当前 splits 与 traits 计算活跃变体。其底层实现variation.ts会注入pageUrl特征并用plasmic_seed作为随机源通过种子化随机函数保证分桶稳定extractPlasmicQueryData在服务端预取并缓存页面查询数据避免客户端重复请求返回revalidate: 60启用 ISR让页面在 60 秒后按需重新生成。4.3 渲染变体透传export default function PlasmicLoaderPage(props: { plasmicData?: ComponentRenderData; queryCache?: Recordstring, any; variation?: Recordstring, string; }) { const { plasmicData, queryCache, variation } props; const router useRouter(); if (!plasmicData || plasmicData.entryCompMetas.length 0) { return Error statusCode{404} /; } const pageMeta plasmicData.entryCompMetas[0]; return ( PlasmicRootProvider loader{PLASMIC} prefetchedData{plasmicData} prefetchedQueryData{queryCache} pageParams{pageMeta.params} pageQuery{router.query} variation{variation} PlasmicComponent component{pageMeta.displayName} / /PlasmicRootProvider ); }variation通过PlasmicRootProvider下发给渲染树Plasmic 会据此决定对每个 split 使用哪个变体分支从而在客户端也保持与服务端一致的变体选择。五、完整请求链路串讲把三个环节串起来一次访问的完整链路如下浏览器发起 GET 请求携带 UTM 查询参数 / UA 头 / plasmic_seed cookie │ ▼ Edge Middlewaremiddleware.ts ├─ 解析 browseruserAgent与 utm_source查询参数 ├─ getMiddlewareResponse 计算目标路径原路径 __pm__ 特征段 ├─ 若无 plasmic_seed随机生成种子并写入 Set-Cookie └─ NextResponse.rewrite 重写到该路径 │ ▼ Catch All 页面[[...catchall]].tsx ├─ 构建期getStaticPaths 已生成该特征组合对应的静态页面 ├─ getStaticPropsrewriteWithoutTraits 剥离特征 │ ├─ maybeFetchComponentData 获取渲染数据 │ ├─ getActiveVariation 依据 splits traits 选出变体 │ └─ extractPlasmicQueryData 预取查询数据ISR revalidate60 └─ 渲染PlasmicRootProvider 以 variation 驱动组件变体由于重写发生在边缘网络、页面在构建期已静态化因此变体判定几乎没有额外延迟代价是特征取值必须在构建期预知并枚举进getStaticPaths。六、变体方案选型参考仓库中还有其他目标定位示例可用于对比选型方案路径生成变体判定时机适用场景build-time-targeting本文generateAllPathsWithTraits构建期穷举边缘 SSR特征取值有限且预先可知追求极致首屏性能custom-targeting不枚举运行时动态边缘重写特征取值无法穷举如 UTM 任意值custom-targeting-codegen代码生成模式构建期/运行时使用 Plasmic Codegen 而非 Loader 的工程若特征取值空间很大例如utm_source可能是任意字符串穷举所有组合会指数级膨胀路径数量此时更适合 custom-targeting 的运行时方案而本例取值受限browser三选一、utm_source二选一构建期穷举是高效且可预测的选择。七、本地运行与验证# 安装依赖并启动开发服务器 yarn install yarn dev项目脚本见 package.jsondevnext dev、buildnext build、startnext start、lintnext lint。依赖方面核心是plasmicapp/loader-nextjs^1.0.333与 Next.js 16.2.1。本地验证变体效果的步骤yarn dev启动后在浏览器访问根路径默认看到无特征版本在 URL 后追加?utm_sourcegoogle中间件会把utm_sourcegoogle编码进重写路径页面展示utm_source google变体在 DevTools 中切换 User-Agent 为 Chrome/Safari 或其他浏览器观察browser特征对应的变体首次访问后检查 cookie确认plasmic_seed已写入且保持不变多次刷新应始终看到同一个 A/B 测试桶。如需在 Plasmic Studio 中查看/克隆对应项目其项目 ID 为qSU617xDVJeD8V18Bsr4AA见 plasmic-init.ts可在设计器中调整两个变体的内容后发布前端重新构建即可生效。八、小结构建期目标定位示例展示了一条完整可落地的变体链路特征声明PLASMIC.registerTrait注册browserchoice 枚举与utm_sourcetext 自由文本边缘重写getMiddlewareResponse将请求特征与随机种子编码进路径并维护plasmic_seedcookie 保证分桶稳定构建期穷举generateAllPathsWithTraits结合已知特征取值与 16 个种子生成全部静态路径服务端解析rewriteWithoutTraits剥离特征、getActiveVariation依据 splits 与 traits 选出变体、extractPlasmicQueryData预取数据渲染透传variation经PlasmicRootProvider驱动组件分支。这套模式适合特征取值有限、预先可知且对首屏性能敏感的场景若特征取值不可穷举可参考仓库中的 custom-targeting 运行时方案。两者的底层路径编码、种子分桶逻辑都统一实现在 packages/loader-edge/src/variation.ts值得通读以理解 Plasmic 变体体系的完整设计。赞分享低代码前端后端【免费下载链接】plasmicVisual builder for React. Build apps, websites, and content. Integrate with your codebase.项目地址https://gitcode.com/gh_mirrors/pl/plasmic点击查看免费下载相关推荐KubeVela 自定义组件与自定义运维特征实战基于 CUE 定义 StatefulSet 与存储卷 Trait 的完整示例KubeVela 自定义组件与自定义运维特征实战基于 CUE 定义 StatefulSet 与存储卷 Trait 的完整示例 本文以 KubeVela 仓库云原生DevOps运维微服务gemma-2-2b-it-MT-SimPO社区贡献指南如何快速参与开源AI翻译模型开发gemma 2 2b it MT SimPO社区贡献指南如何快速参与开源AI翻译模型开发 欢迎来到gemma 2 2b it MT SimPO项目的社区贡献指OrcaSlicer 安装配置完全指南从零到第一次成功打印OrcaSlicer 安装配置完全指南从零到第一次成功打印 这是一篇面向新手用户的 OrcaSlicer 上手教程。OrcaSlicer 是一款开源的 3D桌面应用3D渲染图形学上一篇Orca 渲染进程 Agent 状态高频路径的性能优化从 9,279 个监听者到单次发布的事务化折叠下一篇终极MasterPortfolio安全指南保护你的个人数据和GitHub信息的7个关键步骤创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑