@builder.io/sdk-angular 从零到实战:Builder.io Gen2 Angular SDK 能力全景与版本演进解析
【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder
导读
本文以@builder.io/sdk-angular(Builder.io Gen2 Angular SDK)的完整变更记录为主线,结合本仓库packages/sdks/output/angular的产物与packages/sdks/src的 Mitosis 源码,系统讲解该 SDK 的内容获取、渲染、个性化、A/B 测试、可视化编辑与多运行时构建能力。读完本文,你将掌握fetchOneEntry/fetchEntries的完整参数体系、Content组件的正确用法、自定义组件注册协议,以及如何为 Angular 17+ 应用接入 Builder 无头可视化开发能力。
SDK 定位与安装
@builder.io/sdk-angular是 Builder 的 Gen2 Angular SDK,与 React、Vue、Svelte、Qwik 等 SDK 一样,由 Mitosis 从同一份跨框架源码编译生成,其 Mitosis 源位于 packages/sdks/src。当前仓库中的产物版本为 0.25.13,见 packages/sdks/output/angular/package.json。
安装与版本约束:
npm install @builder.io/sdk-angular- 该 SDK 要求 Angular 版本
>=17.3.0,peer 依赖为@angular/common与@angular/core(见 package.json)。这一点与 0.21.0 的"🚨 Breaking Change"直接相关:SDK 全面转向 Angular 17+ 的信号(signals)体系。 - 运行时依赖仅有
isolated-vm(用于 Node 端代码块解释执行)与tslib。isolated-vm在 0.23.0 从5.0.0升级到6.0.0以支持 Node v24,同时弃用 Node 18 与 Node 20,因此生产环境需使用 Node 22+(0.0.5 时支持 Node v22)或 Node 24。 - 包通过条件导出(exports)提供
node/browser/edge/edge-routine/netlify多种入口,分别指向lib/node、lib/browser、lib/edge三个构建产物,对应多 bundle 支持(0.1.0 引入)。
内容获取:fetchOneEntry 与 fetchEntries 完整参数
SDK 的核心数据入口是fetchOneEntry(返回单条内容,内部调用fetchEntries并取limit: 1的首项)与fetchEntries(返回分页数组)。其实现位于 packages/sdks/src/functions/get-content/index.ts,参数类型定义在 packages/sdks/src/functions/get-content/types.ts。
import { fetchOneEntry } from '@builder.io/sdk-angular'; const content = await fetchOneEntry({ apiKey: 'YOUR_API_KEY', model: 'page', userAttributes: { urlPath: '/', device: 'mobile', }, });GetContentOptions的核心参数:
| 参数 | 类型 | 默认值 / 说明 |
|---|---|---|
model | string | 必填,要获取内容的模型名 |
apiKey | string | 必填,公开 API Key |
limit | number | 1(fetchOneEntry 固定为 1) |
offset | number | 0,分页偏移 |
userAttributes | Record<string, any> | 用户属性,用于定向(如urlPath、returnVisitor、device),随userAttributesJSON 参数发送 |
query | Record<string, any> | MongoDB 风格查询,如{ data: { myCustomField: { $gt: 20 } } },会被扁平化后写入 URL |
locale | string | 自动解析本地化字段(见 0.17.1、0.19.3 的 locale 修复) |
enrich | boolean | 是否解析多级引用 |
enrichOptions | EnrichOptions | 约束引用解析行为(0.25.13 新增) |
fields/omit | string | 字段白名单/黑名单,omit优先于fields |
canTrack | boolean | true,置 false 时禁用 A/B 测试与 Cookie 定向 |
cacheSeconds | number | 缓存秒数,写入 Cache-Control max-age |
staleCacheSeconds | number | stale-while-revalidate 的陈旧缓存时长 |
sort | { [key]: 1 \| -1 } | 排序,如{ createdDate: 1 } |
includeUnpublished | boolean | false,是否包含草稿 |
apiVersion | 'v3' | 目前仅支持v3 |
apiHost | string | 默认https://cdn.builder.io(0.2.23 新增) |
fetch/fetchOptions | — | 覆盖全局 fetch 及其 init 参数 |
URL 的组装逻辑在 generate-content-url.ts 中可查证:缺少apiKey会直接抛出Missing API key;非v3的apiVersion会抛出Invalid apiVersion;omit的默认值是meta.componentsUsed(对应 0.18.10 的修复);noTraverse参数在limit !== 1时为true以优化列表性能。
enrichOptions:精细控制引用解析(0.25.13)
EnrichOptions(types.ts)允许在开启enrich后约束引用解析的深度与字段:
await fetchOneEntry({ apiKey, model: 'page', enrich: true, enrichOptions: { enrichLevel: 2, // 嵌套引用最多解析 2 层 model: { product: { fields: 'id,name,data.price', // 每模型包含字段 omit: 'data.blocks', // 或省略字段 }, }, }, });其中enrichLevel决定嵌套引用解析层数,层数越高响应体越大,应尽量取业务所需的最低层数;model按模型名提供fields/omit白黑名单。这些约束不仅影响请求,还会同步告知 Visual Editor 站点使用了哪些约束条件。只有enrich: true时该选项才会被序列化进enrichOptions查询参数(见 generate-content-url.ts)。
错误处理语义(0.17.0)
0.17.0 是一个 Breaking Change:fetchEntries与fetchOneEntry此前会吞掉一切错误并返回null;此后,fetch抛出的任何错误、或 Builder API 返回的任何非成功响应,都会直接向上抛出(见 index.ts 中throw content的实现)。这意味着调用方必须自行 try/catch,并根据null(未命中)与异常(请求失败)区分两种情形。
渲染:Content 组件与标准接入方式
获取到内容后,通过<builder-content>组件渲染。SDK 的 README(packages/sdks/output/angular/README.md)给出了一个完整的独立组件示例:
import { Component } from '@angular/core'; import { Content, fetchOneEntry, type BuilderContent } from '@builder.io/sdk-angular'; @Component({ selector: 'app-catchall', standalone: true, imports: [Content], template: ` @if (content) { <builder-content [model]="model" [content]="content" [apiKey]="apiKey"></builder-content> } @else { <div>404 - Content not found</div> } `, }) export class CatchAllComponent { apiKey = 'YOUR_API_KEY'; model = 'page'; content: BuilderContent | null = null; async ngOnInit() { const urlPath = window.location.pathname || ''; const content = await fetchOneEntry({ apiKey: this.apiKey, model: this.model, userAttributes: { urlPath }, }); if (!content) return; this.content = content; } }要点:
- 组件选择器是
builder-content:0.2.14 将导出选择器修正为builder-content以兼容 Angular v18;0.2.10 也将content-variants选择器改回content。 model与content是必填 props(0.18.0 Breaking Change),apiKey可额外传入。nonceprop(0.2.1):可为 SDK 内联生成的style/script标签设置nonce属性,便于与 CSP 策略配合。BlocksWrapperProps(0.18.13):允许为单个<blocks>实例覆写全局 props,覆盖时局部 props 完全替换全局 props,除非手动合并(详见 CHANGELOG 中的builderContext.BlocksWrapperProps合并示例)。- SSR 注意事项:0.17.4 起,Angular SSR v17+ 应用从 Content 层面跳过 hydration,因为 Angular 不支持对动态创建的元素做 hydration;同时 0.23.1 修复了从 SSR 渲染页面路由跳转时的 hydration 问题。
信号驱动重构:Angular 17+ 的响应式基础(0.21.0)
0.21.0 是 SDK 历史上最重要的重构(🚨 Breaking Change):整个 SDK 迁移到 Angular v17+ 的信号体系(signals、computed、inputs、声明式语句)。CHANGELOG 明示了两个收益:
- 重算时机收窄:只有当依赖的信号更新时才重新计算,而不是每个变更检测周期都重算;
- 渲染范围收窄:带子组件的组件不再因任何变更而整棵重渲染,消除了 Visual Editor 中的卡顿交互。
从 0.25.7 的修复可以进一步印证这一内部机制:props.content更新钩子被编译为 Angulareffect(),它跟踪函数体内实际读取到的所有信号而非声明的依赖列表,因此任何无关的 context 更新都可能重新触发合并逻辑,导致编辑器里的改动被页面初次加载的props.content覆盖。修复方式是为合并加上prevContent守卫,与相邻的props.data、props.locale钩子采用同一模式。
配套的版本门控在 0.25.4:被弃用的allowSignalWriteseffect 选项不再被无条件丢弃,而是按运行时 Angular 版本(VERSION.major < 19)决定是否传递,从而在 Angular 19+ 上消除弃用告警,同时保持对低版本的向后兼容。
自定义组件注册:registerComponent 与 ComponentInfo
SDK 允许注册自定义组件供可视化编辑器使用,注册信息通过builder.registerComponent消息序列化后发送给编辑器(见 register-component.ts)。类型定义在 packages/sdks/src/types/components.ts,其中包括:
name:唯一组件名;可以用同名注册覆盖内置组件(如Text);inputs:输入 schema(含type、required、autoFocus、bubble、defaultValue等字段);group?: string(0.25.10 新增):把自定义组件归入编辑器插入菜单中自己的折叠分组,相同group的组件聚合在一起,未设置或为空时回退到默认的 "Custom Components" 分组;canHaveChildren/noWrap/fragment:子节点与包裹元素行为;models:限定组件可用的模型列表(0.2.3 修复);requiredPermissions:按用户权限限制组件可见性;override:覆盖内置组件时跳过默认特殊行为(如 Image 的宽高比编辑器、Columns 的列编辑器);hooks:自定义生命周期钩子;meta(0.17.1 新增):组件元信息。
shouldReceiveBuilderProps:精确控制 Builder props 注入
注册组件的shouldReceiveBuilderProps配置决定 SDK 是否向组件注入builderBlock、builderContext、builderComponents、builderLinkComponent四个 Builder props。
- 0.1.0 引入时默认值:
builderBlock: true、builderContext: true、其余false,并给出按需覆写的示例(如只保留builderBlock与builderComponents); - 0.2.0 将其改为全部默认
false——默认不再注入任何 Builder props,只有显式打开对应开关才注入。
仓库中的内置组件是很好的参照:accordion/component-info.ts、columns/component-info.ts、form/component-info.ts、symbol/component-info.ts 四者都全量开启;button/component-info.ts 只开启builderLinkComponent;image/component-info.ts 只开启builderBlock。
输入回调与序列化
- 0.2.19 为自定义组件输入的
onChange增加了第二个参数previousOptions,回调在旧 options 状态下触发前保留快照,且支持异步函数(0.17.1); - 0.2.9 / 0.2.5 保证注册信息与插件中的函数(如字段上的
showIf函数)可以被正确序列化;0.25.11 进一步让showIf回调能接收父级参数并通过context.locale读取编辑器当前 locale; - 0.25.10 同时修复了 Image 组件在 alt 文本为空时渲染显式空
alt属性,避免无障碍语义丢失。
个性化与 A/B 测试
setClientUserAttributes:写入用户属性 Cookie
setClientUserAttributes自 0.17.7 导出,用于设置 Builder 的用户属性 Cookie,该 Cookie 被 Personalization Containers(个性化容器)用来决定渲染哪个变体:
import { setClientUserAttributes } from '@builder.io/sdk-angular'; setClientUserAttributes({ device: 'tablet', });其底层实现在 packages/sdks/src/helpers/user-attributes.ts:调用userAttributesService.setUserAttributes,把新属性与已有属性合并后写入名为builder.userAttributes的 Cookie,并通知所有订阅者;在非浏览器环境(SSR)下直接跳过写入。注意canTrack: false时不会写入 Cookie,0.0.6 还修复过 Symbols 中canTrack=false不被尊重的问题。
A/B 测试的脚本注入策略
A/B 测试(0.18.3 引入支持)相关内联脚本的注入经历了一个"引入 → 去重 → 回退 → 再修复"的演进:
- 0.25.2 与 0.25.3:先移除 DOM 中重复插入的 A/B 测试脚本,随即因引发 hydration 回归而回退;
- 0.25.5:
window.builderIoAbTest/window.builderIoRenderContent初始化脚本改为仅在 Content 实际渲染 A/B 变体时才注入,且定义为幂等、在 hydration 目标上自移除,从而在不做客户端 DOM 变更的前提下避免重复注入; - 0.25.9:同样的策略推广到个性化脚本——
window.builderIoPersonalization/window.filterWithCustomTargeting/window.updateVisibilityStylesScript此前每个顶层Content都会注入一次(无论是否存在 Variant Container),现在只由实际包含个性化块的Content注入,定义同样幂等。
另外,fetchEntries/fetchOneEntry在浏览器端会通过handleABTesting在客户端导航场景直接处理 A/B 测试(见 get-content/index.ts);0.18.2 修复了/track调用在默认与变体场景下重复上报的校验逻辑;0.24.1 修复了trackConversion方法。0.25.12 修复了 Builder Studio 定向请求中布尔型用户属性的处理。
可视化编辑:subscribeToEditor 与编辑器通信
可视化编辑依赖 SDK 与编辑器 iframe 之间的 postMessage 通信。0.18.0 对subscribeToEditor做了破坏性改造:参数从位置参数改为具名参数对象,且apiKey变为必填:
// 旧写法(已废弃) subscribeToEditor('page', () => { ... }, { trustedHosts: ['...'] }); // 新写法 subscribeToEditor({ apiKey: '...', model: '...', trustedHosts: ['...'], callback: () => { ... }, });编辑器消息的接收端在 packages/sdks/src/helpers/subscribe-to-editor.ts:每次收到 MessageEvent 都会先经过isFromTrustedHost(trustedHosts, event)校验,然后按消息类型分发到builder.configureSdk、builder.triggerAnimation、builder.resetState、builder.contentUpdate等回调。0.25.6 加强了来源校验——改用精确的可信主机名比对,并拒绝格式非法或非 HTTP(S) 的 origin(0.0.8 起就要求先确认e.origin是合法 URL)。
可视化编辑相关的历史修复还包括:
- 0.18.1:编辑器 iframe 中修改输入值能即时触发变更;
- 0.18.4:Custom Code 块的代码修改实时反映、修复
nativeElement找不到的问题(0.17.3 起支持嵌入 iframe); - 0.18.6 / 0.18.7 / 0.18.9:修复可视化编辑时新子块被添加到顶部的问题(Section 等使用 children 渲染的组件),0.18.7 引入
ngAfterContentChecked钩子并补充详尽注释; - 0.25.7:修复编辑器改动被初次渲染内容覆盖的回归(即前文
effect()跟踪问题); - 0.18.12:Symbol 条目在编辑器中变化时加载正确内容;
- 0.17.2:支持在 Builder Visual Editor 的 Studio 标签中预览内容。
图像与多媒体块的能力演进
Image / Video / RawImg 块的优化贯穿整个版本历史,是性能与功能并重的典型模块:
- 响应式图片:0.19.0 为 RawImg 组件添加
srcset,并给 Video 组件接入 Intersection Observer 实现视口懒加载;0.19.1 给 RawImg 加loading="lazy";0.25.8 在 Gen2 SDK 中暴露 Image 的sizes字段并修复响应式源的选择逻辑; - 加载策略:0.0.7 为 Image 块增加
highPriority选项实现 eager 加载;0.17.9 为 video 元素增加懒加载;0.17.6 移除 Video 块的 z-index(避免遮挡子元素); - 文件类型:0.0.9 支持 Image 块上传
webp,0.17.1 扩展了 Image/Video 块允许的文件类型;0.0.9 同时移除了 Embed 块逻辑中硬编码的iframelyAPI key;0.2.3 修复 SVG 图片冗余srcset;0.18.8 为图片增加title选项; - 表单块:0.0.10 支持 TextArea 块并修复 Select/TextArea 的
required选项;0.17.5 修复 Form 块重复渲染 children;0.18.15 修复表单提交错误处理;0.20.1 修复表单提交应使用 radio 的值而非 name。
运行时与多环境构建:node / browser / edge
SDK 针对不同运行环境产出独立 bundle(0.1.0 引入多 bundle 支持),构建脚本见 packages/sdks/output/angular/package.json:build依次执行build:node、build:browser、build:edge,产物输出到lib/下的node/browser/edge三个目录。
- Node 端代码执行:依赖
isolated-vm沙箱解释执行 JS 代码块(见 packages/sdks/src/functions/evaluate/node-runtime)。0.2.17 修复了 arm64 机器运行 Node 20 时禁用initializeNodeRuntime()的问题;0.24.0 消除长时间运行 Node.js 进程中的内存泄漏;0.22.3 保证只有导入 edge build 时才使用 edge runtime。 - SSR 支持:0.1.0 为 A/B 测试与 Symbols 提供 SSR;0.2.2 将
onInit转换为服务端与客户端都执行的ngOnInit,而onMount/onUpdate只在浏览器执行(带浏览器判断的ngOnInit/ngOnChanges);0.2.24 修复 Builder children 块未被 SSR 的问题。 - 调试:0.2.26 支持在
process.env.DEBUG=true时记录 SDK 命中的每个 API URL。
版本演进路线图
从 0.0.1 的 alpha 至今,SDK 的演进可归纳为几个关键节点:
| 版本 | 里程碑 |
|---|---|
| 0.0.1 – 0.0.10 | alpha 起步:Angular 16.2+ 支持、多 bundle、TextArea/WebP 等基础能力 |
| 0.1.0 – 0.2.0 | shouldReceiveBuilderProps引入并默认收敛为全关闭;SSR A/B 测试与 Symbols |
| 0.17.0 – 0.18.0 | 获取 API 抛错语义、subscribeToEditor具名参数化、model/content必填、A/B 测试支持 |
| 0.21.0 | 全面信号化重构,最低 Angular 17.3.0 |
| 0.22.x – 0.25.x | 脚本注入去重与幂等、Visual Editor 修复、group分组、enrichOptions |
对照 CHANGELOG(packages/sdks/output/angular/CHANGELOG.md)可以看到,每一次 Patch 都与具体的源码行为一一对应,这也是将 CHANGELOG 作为 SDK 行为说明书使用的价值所在:它记录了每个参数的引入时机(如apiHost在 0.2.23、nonce在 0.2.1)、每个 Breaking Change 的迁移方式,以及大量可视化编辑、SSR、个性化场景下的边界修复。升级 SDK 前,对照该文件逐条排查影响面,是避免回归的最直接手段。
实践建议总结
- 内容获取:优先使用
fetchOneEntry+userAttributes(至少包含urlPath)完成首屏渲染;列表场景使用fetchEntries配合limit/offset/query/sort;需要引用解析时开启enrich并用enrichOptions收敛体积。 - 错误处理:0.17.0 之后
fetchOneEntry可能抛异常,务必 try/catch,并用null与异常区分"无内容"与"请求失败"。 - 组件注册:自定义组件默认不再收到任何 Builder props,按需开启
shouldReceiveBuilderProps;用group归组插入菜单项;用models限制可用模型。 - 个性化与 A/B:通过
setClientUserAttributes在客户端更新定向属性;理解脚本注入的幂等策略,避免页面多Content时的重复脚本问题。 - 运行时选型:Node 24 / 22 环境使用 node build,边缘函数场景按条件导入 edge build;Angular 应用需确保版本
>=17.3.0。
【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考