Builder.io SDK 体系完全指南:Mitosis 驱动的多框架视觉开发 SDK 架构与实践
【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder
本篇文章以 packages/sdks/README.md 为纲领,系统讲解 Builder.io 新一代 SDK(gen2 SDKs)的整体架构、代码生成原理、开发调试流程与发布机制。作为一套由 Mitosis 生成的"一套源码、多框架输出"的 SDK 体系,它同时覆盖 React、Vue 3、Svelte、SolidJS、Angular、Qwik、React-Native 与 Next.js 等多个主流框架;读完本文,你将理解它的 monorepo 布局、渲染组件链路、多环境 bundle 策略、集成测试与发布工作流,并掌握本地开发、调试与在 Apple Silicon + Node v20 环境下规避isolated-vm兼容性问题的方法。
一、SDK 体系概览:什么是 gen2 SDKs
Builder.io 的 SDK 分为两代:第一代(gen1)是手写的@builder.io/react与@builder.io/core;第二代(gen2)则是本文的主角——由 Mitosis 自动生成的新一代 SDK 集合,它们共享同一套 Mitosis 源码,再由构建工具翻译为各框架的目标代码。
当前仓库支持以下 gen2 SDK:
- React-Native
- Vue 3
- Svelte
- SolidJS
- Angular
- Qwik
- React
- NextJS(实验性,支持 React Server Components 注册)
从仓库目录结构可以直观印证这套"一份源码、多份输出"的设计:所有 SDK 的生成代码统一存放在 packages/sdks/output 目录下,其中包含angular/、nextjs/、qwik/、react/、react-native/、solid/、svelte/、vue/八个子目录;而 Mitosis 的原始源码则位于 packages/sdks/src。也就是说,开发者只需要维护src/下的 Mitosis 组件,任何一次修改都会通过代码生成同步到全部八个框架 SDK。
注意:虽然 monorepo 根目录与整个仓库根目录相同,但并非仓库内所有子目录都属于该 monorepo。只有
packages/sdks、packages/react、packages/core、packages/react-tests、packages/sdks-tests是 yarn workspace 的组成部分(详见 docs/ARCHITECTURE.md)。
二、Monorepo 与构建编排
2.1 基于 Yarn v3 + Nx 的仓库结构
gen2 SDK 与 gen1 React SDK 位于同一个 monorepo 中,使用 Yarn v3 workspaces 与 Nx 进行配置。monorepo 根节点名为@builder.io/root,由以下几部分构成:
| 包 | 职责 |
|---|---|
packages/sdks | gen2 SDK 及其全部集成测试 |
packages/react | gen1 React SDK |
packages/core | gen1 core SDK(被 gen1 React SDK 使用) |
packages/react-tests | gen1 React SDK 集成测试 |
packages/sdks-tests | gen1 与 gen2 SDK 共用的集成测试 specs 与 Playwright 配置 |
2.2 Nx 的作用
Nx 负责解决 monorepo 中大量相互依赖的构建复杂度。构建时统一使用yarn g:nx build而非裸yarn build,这样 Nx 会先执行所有前置步骤,保证当前build命令的依赖处于最新状态,从而大幅简化本地开发与测试。同时 Nx 会对每一步构建结果做缓存,重复执行yarn g:nx build时只会重跑发生变化的部分。
如需直观查看各构建任务之间的依赖关系,可以在 monorepo 任意位置执行yarn g:nx graph启动依赖关系图查看器。
2.3 gen2 SDK 的整体构建流程
从 docs/ARCHITECTURE.md 可以梳理出完整的构建链路:
- 先构建 gen1 依赖:gen2 SDK 依赖 Mitosis,而 Mitosis 又依赖 gen1 React SDK,因此必须先构建
@builder.io/sdk(core)与@builder.io/react(react gen1),之后才能运行 Mitosis 构建; - 运行 Mitosis 构建:将 Mitosis 源码生成到 packages/sdks/output 下各 SDK 的源码目录;
- 逐框架构建:每个 gen2 SDK 使用各自框架的构建工具链独立打包(部分用 Vite/Rollup,部分需要针对目标框架的专用库);
- 构建并运行集成测试:测试依赖 SDK 先构建完成,同时也依赖位于 packages/sdks-tests 的集成测试规格。
2.4 每个 SDK 的多环境 bundle
由于部分 SDK 代码与运行环境强相关(尤其是执行任意 JS 代码的evaluator),每个 SDK 需要为三种运行时分别生成 bundle:浏览器(browser)、Node.js(node)与边缘运行时(edge)。因此每个 SDK 会被构建三次,产物通常位于各 SDK 目录下的lib/edge、lib/node、lib/browser三个子目录。
以 packages/sdks/output/svelte/package.json 为例,其exports字段针对不同环境条件做了精细化映射:
"exports": { ".": { "svelte": "./lib/browser/index.js", "node": "./lib/node/index.js", "browser": "./lib/browser/index.js", "edge-routine": "./lib/edge/index.js", "workerd": "./lib/edge/index.js", "deno": "./lib/edge/index.js", "lagon": "./lib/edge/index.js", "netlify": "./lib/edge/index.js", "edge-light": "./lib/edge/index.js", "bun": "./lib/edge/index.js", "electron": "./lib/node/index.js", "default": "./lib/browser/index.js", "types": "./lib/browser/index.d.ts" }, "./bundle/edge": "./lib/edge/index.js", "./bundle/node": "./lib/node/index.js", "./bundle/browser": "./lib/browser/index.js" }可以看到,同一份 SDK 在 Svelte 组件环境、浏览器、Node、以及 Deno/Bun/Cloudflare Workers(workerd)/Netlify 等边缘环境之间会自动选择最合适的 bundle 入口。
三、Mitosis:一份源码,八个框架
3.1 Mitosis 配置与插件体系
Mitosis 是这套 SDK 体系的核心生成器,其相关配置集中在 packages/sdks/mitosis.config.js(位于packages/sdks/根目录)中。该配置文件承载了 Mitosis 构建的全部配置,并大量使用 Mitosis 插件系统——多数插件只针对一个或多个输出目标生效,用于在生成过程中对 Mitosis 内容做修改,从而精确产出想要的框架代码。如果在生成代码中看到某些变化原因不明,mitosis.config.js 是最先应该排查的地方。
3.2 overrides 与 useMetadata
- packages/sdks/overrides:存放每个 gen2 SDK 的覆盖文件。这是"万不得已"的手段,用于某个 SDK 需要完全不同的文件内容时。该方案较为脆弱:一旦原文件发生变化,需要手动同步更新 override,保证导出名、函数参数等完全一致。相对而言,使用 Mitosis 插件修改文件是更安全的方式。
useMetadata():Mitosis 组件中用来传递配置的函数,配置对象既会传给内部 Mitosis 代码库,也会传给 mitosis.config.js 中的插件。
一个重要边界:mitosis.config.js中的插件只作用于 Mitosis 组件文件(.lite.tsx),不会处理普通.ts文件;后者会被原样复制到outputs目录。
3.3 源码组织
gen2 SDK 的 Mitosis 源码目录 packages/sdks/src 主要分为两大块:
src/blocks:所有 Builder blocks(Text、Image、Columns 等);src/components:渲染 SDK 代码所需的 Builder 组件。
从实际目录看,src/components下包含content/、content-variants/、block/、blocks/、dynamic-renderer/、live-edit.lite.tsx、error-boundary.lite.tsx、inlined-script.lite.tsx、inlined-styles.lite.tsx、awaiter.lite.tsx、dynamic-div.lite.tsx等组件,与文档所述渲染链路一一对应。
3.4 渲染组件链路:从 ContentVariants 到 Block
组件之间的调用关系清晰地体现了 SDK 的渲染分层(见 docs/ARCHITECTURE.md):
ContentVariants:处理 A/B 测试,为每个变体调用Content;Content:调用Blocks渲染 block 列表,并用EnableEditor包裹以启用可视化编辑;Blocks:为每个 block 调用Block。
真正渲染单个 block 的逻辑被拆散到多个子组件中,这是为了绕开多个框架的限制(主要是 Svelte 和 React RSC):
RepeatedBlock:当处理重复项列表时,由Block调用;BlockWrapper:渲染 block 的外层包装 HTML 元素(如果存在);ComponentRef:渲染实际的 block 组件;若componentRef指向一个交互式(非 RSC)组件,则再由InteractiveElement包装器承担渲染职责。
这套拆分让同一套 Mitosis 源码能够适配"组件化程度各异"的框架生态,是 gen2 SDK 得以统一 React(含 RSC)、Svelte 等不同运行时模型的关键设计。
四、本地开发与构建实践
4.1 环境准备
在packages/目录下执行yarn安装整个 monorepo 的全部依赖即可(见 docs/DEVELOP.md)。
4.2 构建单个 SDK
使用 Nx 命令构建指定 SDK,例如构建 Svelte SDK:
yarn g:nx build @builder.io/sdk-svelte将svelte替换为其他框架名即可构建对应 SDK(如@builder.io/sdk-vue-3、@builder.io/sdk-react等)。对于 gen1 React SDK,命令为:
yarn g:nx build @builder.io/react4.3 将 SDK 链接到自己的项目
本地调试真实交互场景(例如需要测试与 Query API 的某种难以用 JSON 覆盖的交互)时,可以把 SDK symlink 到示例项目:
# 1. 在 SDK 目录构建(如 packages/sdks/output/svelte,gen1 则是 packages/react) yarn g:nx build # 2. 在 SDK 目录执行 npm link # 3. 在项目目录执行(如 examples/sveltekit) npm link @builder.io/sdk-svelte取消链接只需在项目目录重新执行npm install,即会清除所有 symlink。
React-Native 在 iOS 模拟器中的特殊注意事项:iOS 模拟器不支持 symlink 的包,因此只能手动复制 SDK 目录,且每次代码变更后都要重新复制。在 react-native 示例中提供了yarn run cp-sdk命令来自动完成复制。
4.4 使用本地 Mitosis
如果需要使用尚未合并到上游的 Mitosis 修改,可以:
- 将 BuilderIO/mitosis 克隆为本仓库的兄弟目录(如
my-code/builder/与my-code/mitosis); - 按 Mitosis 的 setup 步骤初始化;
- 在
mitosis/packages/core与mitosis/packages/cli分别运行yarn run start; - 在本仓库执行
yarn run add-symlinks。
之后便会使用本地版本的 Mitosis。提交代码前务必运行yarn run remove-symlinks移除所有 symlink——这不仅适用于packages/sdks,也适用于任何被链接的示例(如 vue-storefront、react-native 示例)。
五、集成测试体系
5.1 编写测试用例
集成测试的最佳实践是:在 Builder 编辑器中创建一条能展示待测功能/Bug 的内容,下载其 JSON,然后:
- 在
src/specs/index.ts中新增一个测试用例(参考已有 specs); - 在
src/e2e-tests中为其添加测试用例。
新增的测试会针对每一个 SDK 与框架组合自动运行。
5.2 运行集成测试
本地运行集成测试的方式:
# 在 packages/sdks/e2e 下的目标服务目录(gen1 则是 packages/react-tests)执行 yarn g:nx test # 或者在 monorepo 任意位置指定服务名 yarn g:nx test @e2e/svelte@e2e/svelte中的svelte可替换为目标服务名。若想一次运行多个测试,可用逗号分隔服务名:
SERVER_NAME=svelte,react,nuxt yarn g:nx test:e2e @sdk/tests此外还有便捷的yarn g:nx e2e:run:*命令,可一步完成构建并运行某个 SDK 的测试。
5.3 Snippet 测试与真实数据测试
- Snippet 测试与 e2e 测试类似:服务端位于 packages/sdks/snippets,测试位于
tests/src/snippet-tests,运行方式为:
SERVER_NAME=svelte,react,nuxt yarn g:nx test:snippet @sdk/testssnippet 测试会向 Builder API 发起真实网络请求,因此可能不稳定;之所以保留这种设计,是为了让 snippet 可以原样共享给客户,无需重写数据获取逻辑。
- 调试测试:加上
--debug标志(如yarn g:nx e2e @e2e/svelte --debug)可在浏览器窗口中以交互式 Playwright 运行测试,此时建议给目标测试加.only,让其余测试暂时被 Playwright 忽略。 - 仅启动服务不跑测试:执行
yarn g:nx serve @e2e/sveltekit(替换服务名)即可。 - 实时数据测试:在 e2e 服务中找到
getProps调用,传入data: "real"参数,即可从 Builder API 拉取真实数据替代 JSON mock 文件。
六、SDK 发布流程
发布遵循Changeset + GitHub Actions工作流,官方明确警告不要手动发布包(详见 PUBLISHING.md)。自动化的原因在于该工作流能够保证:
- 每个 SDK 的 CHANGELOG 正确更新;
- 所有依赖包正确构建;
- 更新每个 SDK 内的
SDK_VERSION常量与待发布版本一致——Visual Editor 正是利用该常量识别页面当前使用的 SDK 版本,便于团队定位用户问题; - 所有 SDK 同步发布,从而在内外沟通时能用统一版本号表述功能(如"
v0.5.9增加了 Nested Symbols 支持",而不是 Qwik 是v0.4.3、Vue 是v0.4.8、NextJS 是v0.5.7这样割裂的版本)。
6.1 标准发布步骤
步骤 1:添加 Changeset。在 PR 中于 monorepo 任意位置执行:
yarn g:changeset按 CLI 指引创建 changeset。
步骤 2:升级版本并发布。PR 合并后,工作流会自动创建版本升级 PR;该 PR 合并后 SDK 即发布到 NPM。如需手动操作:
# 升级版本 yarn g:changeset version # 发布包 yarn g:nx ci:release步骤 5(可选):更新示例:
yarn upgrade-example:all该命令会将所有使用 SDK 的示例升级到刚发布的新版本,保持示例与 SDK 同步。
6.2 发布 dev 版本
仅测试单个 SDK 时,可跳过上述全流程自由发布dev版本:
# 升级到下一个预发布版本(如 0.5.9-1) yarn version prerelease # 构建并以 dev tag 发布 yarn g:nx release --tag=dev七、运行时注意事项
7.1 基于 fetch 的请求
该包使用原生fetch进行数据请求,因此使用时需要目标运行环境(浏览器或 Node.js 等)提供fetch支持;在不支持fetch的老版本运行时中需要自行注入 polyfill。
7.2 Node v20 + Apple Silicon(M1/M2)兼容性
SDK 依赖isolated-vm库来在 Node 服务端安全执行代码。该库在Node v20 + M1 Mac(Apple Silicon)组合下存在兼容性问题。解决方式是为运行服务的命令提供环境变量:
NODE_OPTIONS=--no-node-snapshot如果不提供该标志,SDK 将跳过使用isolated-vm;这一降级行为只会在 Apple Silicon 机器上运行 Node v20 时发生,其他环境不受影响。
从源码也可以印证这一设计:packages/sdks/src/functions/evaluate/node-runtime/ 目录下包含init.ts、node-runtime.ts、safeDynamicRequire.ts等文件,专门封装 Node 运行时中执行任意 JS 的 evaluator 逻辑;而 packages/sdks/output/svelte/package.json 的dependencies中同样声明了"isolated-vm": "^6.0.0",可见该依赖是各 SDK 在 Node/边缘环境下执行自定义 JS(如绑定事件、动态脚本)的基础设施。
7.3 SDK_VERSION 常量
每个 SDK 内部都维护一个SDK_VERSION常量,发布工作流会保证它与即将发布的 npm 版本一致。仓库中的实现位于 packages/sdks/src/constants/sdk-version.ts,并在 packages/sdks/src/helpers/sdk-headers.ts 等请求头相关代码中被引用——SDK 会携带该版本信息发出请求,Visual Editor 据此识别页面当前使用的 SDK 版本,在排障时非常有用。
八、功能特性实现状态
各框架 SDK 的功能并非完全对等,且团队仍在持续迭代。官方提供了三张对照表用于查询各 SDK 的能力差异(见 packages/sdks/README.md 的 Feature Implementation 一节):
- Available Features(可用特性):各框架 SDK 支持的功能对照表;
- Builder Blocks(Builder 区块):各框架可用的 blocks 对照;
- Builder Widgets(Builder 组件):各框架可用的 widgets 对照。
在选用某个框架的 SDK 之前,建议先查阅这些对照表,确认目标框架已覆盖你需要的特性(如 A/B 测试、个性化、Nested Symbols、富文本等),再决定是否采用。
九、参考文档与源码索引
围绕 gen2 SDK 体系,仓库内以下文档与源码可供继续深入:
- packages/sdks/README.md:SDK 总览(本文主文档)
- packages/sdks/docs/ARCHITECTURE.md:monorepo 与构建架构详解
- packages/sdks/docs/DEVELOP.md:本地开发、测试与 symlink 指南
- packages/sdks/PUBLISHING.md:发布工作流
- packages/sdks/src:Mitosis 源码(blocks 与 components)
- packages/sdks/output:八个框架的生成源码
- packages/sdks/src/constants/sdk-version.ts:SDK_VERSION 常量实现
- packages/sdks/src/functions/evaluate/node-runtime/:Node 运行时 evaluator 实现
- packages/sdks/output/svelte/package.json:多环境 bundle 配置示例
【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考