
在 Gatsby 中使用 Builder.io 构建可视化落地页从插件接入、动态页面生成到自定义组件实战【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder本文围绕仓库中的examples/gatsby文档目录展开系统讲解如何通过builder.io/gatsby插件把 Builder.io 的拖拽式可视化建站能力接入 Gatsby 站点。你将掌握插件安装与配置API Key、模板映射、上下文注入、动态条目解析、基于allBuilderModels的 GraphQL 数据查询、动态页面生成与手动页面两种接入模式以及如何把自研 React 组件注册进可视化编辑器最终在 Gatsby 构建时把 Builder.io 中发布的内容批量渲染为静态页面。一、概述Gatsby 与 Builder.io 的集成方式examples/gatsby/README.md是仓库中 Gatsby 相关示例与资源的入口文档它梳理了四条核心资源路径覆盖了从插件、最小示例到完整商业场景的全部集成方式资源说明Builder.io/gatsby 插件用于将 Builder.io 内容源取到 Gatsby 的官方插件Minimal starter 最小示例演示如何在 Gatsby 中使用 Builder.io 构建落地页Gatsby 落地页 Starter落地页起始套件示例如何同时把 Builder 组件作为布局区块header/footer和页面使用Headless Shopify 商店 Starter使用 GatsbyJS 与 Builder.io 构建无头 Shopify 商店前端的起始套件其中前两项在本仓库内均有完整实现是本文展开的主线后两项为仓库文档中提及的外部 Startergatsby-starter-builder与gatsby-builder-shopify本文只作为能力方向引用不展开其外部细节。从实现上讲Builder.io 与 Gatsby 的集成可以归纳为两种互补模式动态页面模式在gatsby-config.js中通过templates把 Builder.io 的某个内容模型model如page映射到本地模板文件Gatsby 构建时根据 Builder.io 中每个条目的 URL 自动生成对应页面手动页面模式不在templates中注册模型改为在src/pages下手写页面组件通过 GraphQL 查询 Builder.io 内容并手动渲染适合把头部、页脚、局部区块等“页面的一部分”接入 Builder.io。二、安装与最小配置2.1 安装插件在 Gatsby 项目中安装官方插件npm install builder.io/gatsby从examples/gatsby-minimal-starter/package.json可以看到该示例使用了builder.io/gatsby^3.0.3并搭配gatsby^5.12.4、react^18.2.0以及builder.io/widgetsBuilder 官方提供的一组预置 widget同时还依赖react-helmet来管理页面head。2.2 注册插件并配置 API Key前往 Builder.io 注册免费账号并在组织页面builder.io/account/organization获取 Public API Key然后在gatsby-config.js中接入插件// In your gatsby-config.js const path require(path); module.exports { plugins: [ { resolve: builder.io/gatsby, options: { // public API Key publicAPIKey: MY_PUBLIC_API_KEY, // OPTIONAL // Set this to true to rely on our cached content. Default value is false, always fetching the newest content from Builder. useCache: false, // OPTIONAL // mapping model names to template files, the plugin will create a page for each entry of the model at its specified url templates: { // page can be any model of choice, camelCased page: path.resolve(templates/my-page.tsx), }, // OPTIONAL mapEntryToContext: async ({ entry, graphql }) { const result await graphql(....); return { property: entry.data.property, anotherProperty: entry.data.whatever, dataFromQuery: result.data /* ... */ }; }, // OPTIONAL // to resolve a single entry to multiple, for e.g in localization resolveDynamicEntries: async (entries) { const entriesToBuild [] for entry of entries { if (entry.data.myprop.isDynamic){ entriesToBuild.push(await myEntryResolver(entry)) } else { entriesToBuild.push(entry) } } return entriesToBuild; }, }, }, ], };2.3 配置项逐个拆解结合packages/gatsby/src/constants.js中defaultOptions与getConfig()的实现各配置项的含义如下publicAPIKey必填Builder 组织页面的公开 API Key。getConfig()中通过invariant(config.publicAPIKey config.publicAPIKey.length 0, ...)强制校验缺失会直接报错。它会被拼进 GraphQL 端点 URL${baseURL}/${publicAPIKey}?cachebusttrue其中baseURL默认为https://cdn.builder.io/api/v3/graphql。useCache可选默认false为true时依赖 Builder 的缓存内容为false时在请求 URL 上追加cachebusttrue始终拉取最新内容。Gatsby 是构建时静态生成因此默认关缓存以拿到最新发布是合理选择。templates可选把模型名camelCase例如page映射到本地模板文件路径的对象。只要传入该配置插件就会为对应模型的每个条目按其在 Builder 中填写的 URL 自动创建 Gatsby 页面见下文“动态页面生成”。mapEntryToContext可选异步函数接收{ entry, graphql }可在创建页面时为页面context注入自定义属性。从packages/gatsby/src/gatsby-node.js的createPagesAsync可以看到它返回的mappedProps会与globalContext一起展开进createPage({ context: { ...globalContext, ...mappedProps } })即模板可以通过 Gatsby 的pageContext拿到这些数据。resolveDynamicEntries可选异步函数接收一整个批次的 entries返回要实际构建的 entries 列表典型场景是本地化——把一个条目按不同 locale 拆成多个页面。在源码中它于filter之后、逐条createPage之前执行。filter可选来自源码默认配置注释同步过滤函数在resolveDynamicEntries之前对 entries 做过滤。仓库注释中给出多店铺场景示例(entry) entry.content.data.store process.env.STORE_TOKEN。globalContext可选来自源码默认配置注释一个对象作为公共 context 注入所有由 Builder 生成的页面源码注释示例为{ store: process.env.STORE_TOKEN }适合多店铺multi-store仓库复用同一套模板。custom404Dev可选来自源码默认配置注释自定义开发环境 404 页面路径。见examples/gatsby-minimal-starter/gatsby-config.js中custom404Dev: path.resolve(src/pages/404.jsx)的用法其目的见gatsby-node.js的onCreatePage是覆盖 Gatsby 默认的/dev-404-page/让本地开发时在 Builder 里新建页面无需重启gatsby develop即可预览。limit可选默认30每次 GraphQL 分页拉取的条目数上限。getConfig()中通过invariant(config.limit 101, ...)强制上限为 100。2.4 最小示例的实际配置仓库中的最小示例examples/gatsby-minimal-starter/gatsby-config.js给出了一个可直接照搬的完整形态const path require(path); module.exports { siteMetadata: { title: Gatsby Minimal Starter, siteUrl: https://www.yourdomain.tld, }, plugins: [ gatsby-plugin-typescript, { resolve: builder.io/gatsby, options: { publicAPIKey: jdGaMusrVpYgdcAnAtgn, useCache: false, custom404Dev: path.resolve(src/pages/404.jsx), templates: { // Render every page model as a new page using the /page.tsx template // based on the URL provided in Builder.io page: path.resolve(src/templates/page.jsx), }, }, }, ], };该示例的package.json还提供了完整的脚本集npm run develop即gatsby develop、npm run buildgatsby build、npm run servegatsby serve与npm run cleangatsby clean。把gatsby-config.js中的 API Key 换成你自己的即可把它当作 Builder.io 集成实验场。三、动态页面生成模板映射与批量建页3.1 工作原理当配置了templates后插件会在 Gatsby 构建时遍历 Builder.io 中对应模型的所有条目为每个“已发布”的条目按其在内容里填写的 URL 创建页面。核心逻辑在packages/gatsby/src/gatsby-node.js中createPages读取options.templates的 key 作为模型名列表初始化全 0 的offsets调用createPagesAsynccreatePagesAsync通过fetchPages以limit offset方式分页拉取各模型内容fetchPages中给每条查询附加options: { cacheSeconds: 2, staleCacheSeconds: 2 }当某批返回条数等于limit时继续递归翻页直到取完每个模板路径会通过fs.existsSync校验存在性缺失时invariant报错builder.io/gatsby requires a valid template path for each model对每个条目调用getPaths(content)从内容的query中提取property urlPath的值作为目标路径单个字符串会被包装成数组仅当路径非空且entry.content.published published时才createPage路径列表逐条创建并把globalContext与mapEntryToContext返回的映射合并进context。由此可以推断你在 Builder.io 里给page模型新建条目并填写 URL如/about点击 Publish 后下一次gatsby build就会自动生成对应的静态页面无需手写任何路由代码。3.2 模板文件长什么样最小示例的模板examples/gatsby-minimal-starter/src/templates/page.jsx是动态页面模板的范例import * as React from react; import { graphql } from gatsby; import { BuilderComponent, builder } from builder.io/react; import { Helmet } from react-helmet; import builder.io/widgets; import Hero from /src/components/Hero/Hero.jsx; import /src/components/Hero/Hero.builder.js; builder.init(jdGaMusrVpYgdcAnAtgn); const PageTemplate ({ data }) { const content data.allBuilderModels.page[0]?.content; return ( Helmet title{content?.data.title}/title /Helmet header/header BuilderComponent content{content} modelpage / footer pA Builder.io starter with Gatsby/p /footer / ); }; export default PageTemplate; export const pageQuery graphql query ($path: String!) { allBuilderModels { page(target: { urlPath: $path }, limit: 1, options: { cachebust: true }) { content } } } ;关键点模板顶部调用builder.init(...)初始化builder.io/reactSDK并导入builder.io/widgets以启用 Builder 的预置 widget页面的pageQuery使用 Gatsby 自动注入的$path变量即插件createPage时写入的路径配合target: { urlPath: $path }精确命中当前条目limit: 1只取一条options: { cachebust: true }保证取到最新内容拿到content后交给BuilderComponent modelpage content{content} /渲染 Builder 可视化的页面结构content?.data.title则可用来设置页面标题。四、手动页面模式用 GraphQL 查询并渲染页面局部如果只想让页面的某一部分如头部导航、某个区块由 Builder.io 驱动就不要把该模型放进templates而是直接在src/pages下手写页面用 GraphQL 查询所需内容。examples/gatsby/README.md关联的示例文档给出了完整范式import React from react; import { graphql } from gatsby; import { BuilderComponent } from builder.io/react; const ExamplePage () { const { header, page } this.props.data.allBuilderModels; return ( div {/* next line assumes you have a header model in builder.io, alternatively you use your own Header / component here */} BuilderComponent modelheader content{header[0].content} / {/* Render other things in your code as you choose */} BuilderComponent modelpage content{page[0].content} / /div ); }; export default ExamplePage; export const pageQuery graphql query { allBuilderModels { # (optional) custom header component model header(limit: 1, options: { cachebust: true }) { content } # Manually grab the example content matching / # For Gatsby content, we always want to make sure we are getting fresh content example( limit: 1 target: { urlPath: / } options: { cachebust: true } ) { content } } } ;这段代码展示了两个 Builder 内容模型header与page/example在同一页面上的混排header模型作为自定义头部区块渲染example模型按urlPath: /精确取到首页内容各自通过BuilderComponent model... content{...} /渲染。注意查询使用limit、target、options.cachebust等参数精确控制取数与新鲜度。关于查询的基础形态插件 README 给出了最小化版本{ allBuilderModels { myPageModel(options: { cachebust: true }) { content } } }这里的allBuilderModels是插件把 Builder.io 远程 GraphQL Schema 挂载到 Gatsby 后的根字段。五、allBuilderModels 背后的 Schema 挂载原理allBuilderModels并非手工定义的字段而是插件在构建期通过createSchemaCustomization动态挂载的第三方 Schema。从packages/gatsby/src/gatsby-node.js与packages/gatsby/src/transforms.js可以看到其完整机制插件基于graphql-tools/wrap对 Builder.io 的远程 GraphQL 端点https://cdn.builder.io/api/v3/graphql/publicAPIKey做 introspection并使用linkToExecutor建立执行链路默认套用三个 TransformStripNonQueryTransform丢弃远程 Schema 中的 Mutation/Subscription只保留 Query、RenameTypes(name \${typeName}_${name})给远程类型加builder前缀避免冲突以及NamespaceUnderFieldTransform把整个远程 Query 命名空间收敛到allBuilderModels 字段之下NamespaceUnderFieldTransform见transforms.js把远程查询类型包装成一个以typeName默认builder命名的嵌套对象类型并新增allBuilderModels根字段其 resolver 会调用context.nodeModel.createPageDependency({ path: context.path, nodeId })注册页面依赖从而让 Gatsby 在增量构建时感知“哪些页面依赖 Builder 内容”sourceNodes会创建一个类型为BuilderGraphQLSource的占位节点ignoreType: true并在非生产环境下按refetchInterval秒定时重建该节点配合上面注册的页面依赖实现开发期的数据刷新createSchemaCustomization还会把 introspection 得到的 SDL 写入 Gatsby 缓存key 形如gatsby-source-graphql-schema-builder-allBuilderModels避免重复 introspection。也就是说从结构上看allBuilderModels就是远程 Builder GraphQL Schema 在 Gatsby 侧的命名空间化入口你在查询中写的模型名如page、header对应 Builder.io 后台的各个内容模型。六、把自研组件接入可视化编辑器Builder.io 的核心价值是可视化编辑而要让编辑器中能拖入你自己的组件需要把组件注册进builder.io/react的组件注册表。插件 README 给出了最简注册示例import { Builder } from builder.io/react; class SimpleText extends React.Component { render() { return h1{this.props.text}/h1; } } Builder.registerComponent(SimpleText, { name: Simple Text, inputs: [{ name: text, type: string }], });Builder.registerComponent(Component, options)中options.name是编辑器侧显示的名字options.inputs声明组件的可配置属性每个 input 至少包含name与type。最小示例中的Hero组件给出了更完整的实战形态。examples/gatsby-minimal-starter/src/components/Hero/Hero.builder.js注册了 Hero 组件并声明了 7 个输入输入名类型默认值说明titlestringYour Title Here标题文本imagefile一张默认占位图 URL图片上传allowedFileTypes: [jpeg,jpg,png,svg,webp]required: truebuttonLinkstringhttps://example.com按钮跳转链接buttonTextstringClick按钮文案heightnumber400区块高度darkModebooleanfalse深色模式开关parallaxStrengthnumber400视差强度advanced: true使其收纳在“show advanced”折叠项下该文件还通过image字段给组件配置了编辑器图标。模板page.jsx中import /src/components/Hero/Hero.builder.js即是在页面加载时完成注册随后编辑器中即可拖入 Hero 并可视化配置上述属性。插件 README 还给出两点进阶建议想体验更丰富的“设计系统 自定义组件”用法参考仓库内的 react-design-system 示例如果希望编辑器的可拖拽范围只限定在你自己的组件上可以开启 Builder 的 components only mode仅 Builder 官方文档中有详细说明此处只作能力提示。七、本地开发与部署的完整工作流结合examples/gatsby/README.md关联文档与最小示例一套完整的落地页开发流程如下注册 Builder.io 并准备 API Key在 builder.io/account/organization 获取 Public API Key替换gatsby-config.js中的演示 Key最小示例内置了演示 KeyjdGaMusrVpYgdcAnAtgn可直接运行看到示例数据运行示例克隆仓库后进入examples/gatsby-minimal-starter执行npm install与npm run start即gatsby develop启动开发服务器连接本地预览开发服务器默认运行在http://localhost:8000在 Builder.io 后台把条目的 Preview URL 指向该地址即可在编辑器里实时预览部署到线上/预发环境后可在 builder.io/models 的模型设置里全局修改 Preview URL具体操作细节见 Builder 官方 models 与 preview url 文档本文不展开在 Builder 中创建并发布内容为page模型新建条目、填写 URL、拖拽组件完成排版后发布重新构建站点执行npm run build或 CI 中的gatsby build插件会在构建期拉取 Builder 内容并为每个已发布条目生成静态页面开发期得益于custom404Dev与页面依赖机制在 Builder 中新建页面后无需重启gatsby develop即可预览。该流程同时覆盖了动态页面第 4、5 步与手动页面第 3 步也可用于验证src/pages下手动查询的页面两种模式你可以按需选择甚至混用布局区块走手动模式内容页走动态模式。八、仓库内可继续深挖的资源插件完整实现packages/gatsby/src/gatsby-node.jsSchema 挂载、页面创建、开发期 404 覆盖、packages/gatsby/src/transforms.jsSchema 变换、packages/gatsby/src/constants.js默认配置与 GraphQL 端点拼装最小可运行示例examples/gatsby-minimal-starter其中 gatsby-config.js 是配置范本src/templates/page.jsx 是动态页面模板src/components/Hero/Hero.builder.js 是自定义组件注册范本更多自定义组件与设计系统集成示例examples/react-design-system本文引用的主题文档本身examples/gatsby/README.md。需要注意的是examples/gatsby/README.md中提及的gatsby-starter-builder与gatsby-builder-shopify属于仓库文档指向的外部 Starter本仓库内未包含其源码若需深入学习其实现应前往对应外部仓库查阅本文仅将其作为能力方向的引子。【免费下载链接】builderVisual Development for React, Vue, Svelte, Qwik, and more项目地址: https://gitcode.com/GitHub_Trending/bu/builder创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考