尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Puck AI + Next.js 实战:用 AI 副驾驶为任意路由生成并发布可视化页面

Puck AI + Next.js 实战:用 AI 副驾驶为任意路由生成并发布可视化页面 Puck AI Next.js 实战用 AI 副驾驶为任意路由生成并发布可视化页面【免费下载链接】puckThe visual editor for React.项目地址: https://gitcode.com/GitHub_Trending/puc/puckPuck 是开源的 React 可视化编辑器它允许你用自己的 React 组件快速搭建页面构建器Puck AI 则在同样的组件体系之上让你通过对话让 AI 组装现有组件、或按需现场生成新组件来产出页面。本指南基于仓库中的recipes/next-ai示例README完整讲解如何在 Next.js App Router 项目中接入 Puck 编辑器与 Puck AI 云客户端实现任意路径 /edit 即进入编辑器、对话生成页面、一键发布、访问即渲染的完整闭环。读完本文你将掌握 config / 编辑器 / 渲染器三要素的协作方式、AI 插件与 Cloud Client 的分工、Assembly / Design 两种 AI 模式的区别以及整套路由重写与数据持久化的实现原理。核心概念Puck 的三要素与 Puck AI 的两部分Puck 的三个组成部分Puck 可视化编辑器由三个主要部分构成config配置、编辑器editor与渲染器renderer。Config注册组件与字段config用于注册用户可以在编辑器中使用的组件以及他们可以编辑的字段。以本 recipe 中的最小配置为例const config { components: { HeadingBlock: { fields: { title: { type: text }, }, render: ({ title }) h1{title}/h1, }, }, };真实仓库中的 puck.config.tsx 在此基础上增加了defaultProps并补充了外层容器样式使其开箱即可编辑import type { Config } from puckeditor/core; type Props { HeadingBlock: { title: string }; }; export const config: ConfigProps { components: { HeadingBlock: { fields: { title: { type: text }, }, defaultProps: { title: Heading, }, render: ({ title }) ( div style{{ padding: 64 }} h1{title}/h1 /div ), }, }, };从源码可以看出两个值得注意的细节ConfigProps提供了泛型类型约束让fields、defaultProps与render之间共享类型安全defaultProps让组件在刚拖入画布时就有合理的初始值避免空白组件。这也是Assembly mode组装模式拼装页面时的零件清单——AI 只能从这套配置中挑选组件。编辑器Puck组件Puck组件渲染编辑器本体。它接收 config将页面导出为 JSON即 Puck 的 Data 结构并接受data作为初始页面数据以编辑已有页面Puck config{config} // 编辑器可用的组件 data{data} // 待编辑的页面 JSON onPublish{(data) { // 将数据保存到你的数据库 }} /本 recipe 中编辑器的实际装配位于 app/puck/[...puckPath]/client.tsx后续会展开讲解它与 AI 插件的集成。渲染器Render组件Render负责渲染页面它需要页面 JSON 以及创建该页面时所用的 configRender config{config} // 创建页面时使用的组件 data{data} // 待渲染的页面 JSON /值得强调的是渲染用的 config 必须与编辑时一致否则页面无法正确还原。在 AI Design mode 下config 还可能是动态生成的——这正是本 recipe 中withDynamicConfig存在的意义见下文 AI 集成部分。Puck AI 的两个组成部分本 recipe 将 Puck AI 作为副驾驶copilot引入它由两部分组成AI 插件浏览器端与Cloud Client服务端。AI 插件编辑器内的聊天界面AI 插件在编辑器中渲染聊天面板并把每条消息发送给你服务器上的 Cloud Clientconst aiPlugin createAiPlugin(); function Editor() { return Puck plugins{[aiPlugin]} config{config} data{data} /; }本 recipe 对插件做了更细致的配置app/puck/[...puckPath]/client.tsxuse client; import type { Data } from puckeditor/core; import { Puck, blocksPlugin, outlinePlugin } from puckeditor/core; import { createAiPlugin, withDynamicConfig } from puckeditor/plugin-ai; import config from ../../../puck.config; const aiPlugin createAiPlugin({ // 允许用户在 design 与 assembly 模式间切换 designMode: { visible: true, }, // 默认选中 design 模式 defaultMode: design, }); // 将 AI 插件置于侧边栏第一个位置 const plugins [aiPlugin, blocksPlugin(), outlinePlugin()]; export function Client({ path, data }: { path: string; data: Data }) { const configWithDesignedComponents withDynamicConfig(config, data); return ( Puck plugins{plugins} data{data} config{configWithDesignedComponents} onPublish{async (data) { await fetch(/api/pages, { method: post, body: JSON.stringify({ data, path }), }); }} / ); }这段代码里有两个关键点createAiPlugin({ designMode: { visible: true }, defaultMode: design })让用户在编辑器中可以切换 Design / Assembly 模式并默认使用 Design 模式。withDynamicConfig(config, data)当 AI 在 Design 模式下生成了新组件后这些组件会被合并进运行时 config。withDynamicConfig正是用来把设计出来的组件动态合并进原有 config 的辅助函数确保Puck与后续的Render都能识别这些新组件。此外onPublish通过 POST 把{ data, path }发送到/api/pages接口完成保存。Cloud Client连接 Puck 云的服务端 APICloud Client 提供将你的服务器连接到 Puck 云的 API。本 recipe 使用了它的puckHandler同类机制实际实现在puckeditor/cloud-client包中——它接收每条聊天消息、转发给 Puck 云并把响应流式返回给浏览器中的插件const handleRequest (request: NextRequest) { return puckHandler(request, { ai: { context: We are Google. You create Google landing pages., }, }); }; export const DELETE handleRequest; export const GET handleRequest; export const POST handleRequest;本 recipe 的完整实现位于 app/api/puck/[...all]/route.tsimport type { NextRequest } from next/server; import { puckHandler } from puckeditor/cloud-client; const handleRequest (request: NextRequest): PromiseResponse { return puckHandler(request, { ai: { // 替换为你的业务上下文 context: We are Google. You create Google landing pages., designMode: { // 允许 AI 使用 design mode 生成新组件 allowed: true, // 约束组件生成规则替换为你自己的指令 instructions: #### Color Palette Always use the following colors: * Primary: \#1976d2\ * Secondary: \#9c27b0\ , }, }, }); }; export const DELETE handleRequest; export const GET handleRequest; export const POST handleRequest;这里的[...all]动态路由段保证所有来自 AI 插件的请求无论方法或路径后缀都会落到同一个处理器上。context是注入 AI 的全局业务语境designMode.instructions则用来约束 AI 生成组件时的风格如示例中的品牌色板。Puck AI 的两种工作模式Puck AI 可以通过两种方式构建页面Assembly mode组装模式只用你 config 中已有的组件来拼装页面行为可预测、样式可控Design mode设计模式当现有组件不足以表达意图时AI 可以按需生成全新组件。本 recipe 开箱即用地启用了Design mode——通过插件侧的defaultMode: design、designMode.visible: true以及 Cloud Client 侧的designMode.allowed: true三处配置共同完成。运行 recipe从 API Key 到发布页面1. 添加 Puck API Key先创建账户并生成 API Key然后在.env.local文件中写入PUCK_API_KEYyour-api-keyAI 插件与 Cloud Client 之间的鉴权依赖该密钥请勿提交到版本库。2. 启动开发服务器在recipes/next-ai目录下运行npm run dev根据 package.json该命令实际执行next dev --turbo依赖next ^16.2.11与react ^19.2.1并包含puckeditor/core、puckeditor/plugin-ai、puckeditor/cloud-client三个关键包。服务器启动后访问 http://localhost:3000 查看首页访问 http://localhost:3000/edit 用 Puck 编辑它。3. 用 Puck AI 创建页面导航到 http://localhost:3000/edit点击左侧边栏的AI按钮输入提示词prompt并回车AI 即开始组装或生成组件。4. 发布页面页面完成后点击顶部工具栏的Publish保存结果然后访问 http://localhost:3000 查看已发布的页面。更强大的是你可以通过访问/your/path/edit在任意路径下创建页面并发布发布后/your/path路由就会渲染该页面。工作原理/edit 重写、发布与静态渲染的完整链路路由重写URL 以 /edit 结尾时进入编辑器当一个 URL 以/edit结尾时proxy.ts 会将该请求重写到 Puck 编辑器路由app/puck/[...puckPath]/page.tsx。看它的完整实现import { NextResponse } from next/server; import type { NextRequest } from next/server; export async function proxy(req: NextRequest) { const res NextResponse.next({ request: req }); if (req.method GET) { // 将匹配 /[...puckPath]/edit 的路由重写到 /puck/[...puckPath] if (req.nextUrl.pathname.endsWith(/edit)) { const pathWithoutEdit req.nextUrl.pathname.slice( 0, req.nextUrl.pathname.length - 5 ); const pathWithEditPrefix /puck${pathWithoutEdit}; return NextResponse.rewrite(new URL(pathWithEditPrefix, req.url)); } // 禁用 /puck/[...puckPath] 直接访问 if (req.nextUrl.pathname.startsWith(/puck)) { return NextResponse.redirect(new URL(/, req.url)); } } return res; }注意两点设计取舍把/foo/edit重写rewrite为/puck/foo对用户透明同时把任何直接访问/puck开头的请求重定向回首页避免用户绕过 /edit 约定直接命中内部路由。编辑器路由 app/puck/[...puckPath]/page.tsx 会加载已保存的页面数据如果该路径是全新的则使用内置的空数据初始化const EMPTY_PAGE_DATA: Data { content: [], root: { props: { title: , }, }, }; // ... const data getPage(path); return Client path{path} data{data || EMPTY_PAGE_DATA} /; export const dynamic force-dynamic;这段代码还暴露了另一个关键设计编辑器路由强制force-dynamic始终保持动态渲染而前台页面路由则是force-static静态渲染两者通过 proxy 隔离互不影响。发布写入数据库并清理 Next.js 缓存点击Publish时页面数据被发送到/api/pages接口app/api/pages/route.ts。该处理器把 JSON 写入database.json并清理该页面的 Next.js 缓存import { revalidatePath } from next/cache; import { NextResponse } from next/server; import fs from fs; export async function POST(request: Request) { const payload await request.json(); const existingData JSON.parse( fs.existsSync(database.json) ? fs.readFileSync(database.json, utf-8) : {} ); const updatedData { ...existingData, [payload.path]: payload.data, }; fs.writeFileSync(database.json, JSON.stringify(updatedData)); // 清除 Next.js 缓存 revalidatePath(payload.path); return NextResponse.json({ status: ok }); }随后catch-all 路由 app/[...puckPath]/page.tsx 读取同一份数据并用Render渲染它const path /${puckPath.join(/)}; const data getPage(path); if (!data) { return notFound(); } return Client data{data} /; // 强制 Next.js 生成静态页面若需要 headers/cookies 等请求期数据请删除此行 export const dynamic force-static;前台渲染客户端 app/[...puckPath]/client.tsx 同样使用withDynamicConfig合并 AI 生成的组件后交给Renderuse client; import type { Data } from puckeditor/core; import { Render } from puckeditor/core; import { withDynamicConfig } from puckeditor/plugin-ai; import config from ../../puck.config; export function Client({ data }: { data: Data }) { const configWithDesignedComponents withDynamicConfig(config, data); return Render config{configWithDesignedComponents} data{data} /; }数据存取getPage 与 database.json数据读取统一由 lib/get-page.ts 完成——它从database.json中按路径取出页面数据import { Data } from puckeditor/core; import fs from fs; export const getPage (path: string) { const allData: Recordstring, Data | null fs.existsSync(database.json) ? JSON.parse(fs.readFileSync(database.json, utf-8)) : null; return allData ? allData[path] : null; };仓库中现成的 database.json 已经保存了一条示例数据——根路径/下有一个标题为 Edit this page by adding /edit to the end of the URL 的HeadingBlock这就是你首次访问首页时看到的内容。文件职责总览下表来自原 README列出了实现上述流程的全部文件及其职责文件用途puck.config.tsx定义 Puck 与 Assembly 模式可用的组件、字段与默认 props。在此添加你自己的组件。app/puck/[...puckPath]/page.tsx为编辑器加载页面数据。app/puck/[...puckPath]/client.tsx渲染带 AI 副驾驶的编辑器把提示词发送到服务器并发布更改。app/[...puckPath]/page.tsx加载并渲染已发布的页面。app/api/pages/route.ts保存已发布的页面。app/api/puck/[...all]/route.ts处理来自 AI 插件的请求并配置 AI 生成。proxy.ts把以/edit结尾的 URL 路由到/puck/[...puckPath]/page.tsx。lib/get-page.ts从database.json读取页面数据。可用你自己的数据获取逻辑替换。database.json充当本地数据库。可用你自己的数据库方案替换。部署到生产环境前必须完成的五件事在把本 recipe 部署上线之前务必完成以下检查保护编辑器与 API。/edit、/api/pages与/api/puck路由默认是公开的。必须添加身份认证authentication、授权authorization与速率限制rate limits以保护页面数据与 AI 调用额度。编辑器的页面级代码注释也明确提示NB this route is public, and you will need to add authentication。补齐你的组件库。把puck.config.tsx中示例用的HeadingBlock替换为你用户真正需要的组件与字段。它决定了用户在编辑器以及 Assembly 模式里能拼装的全部能力。设置你的业务上下文。把app/api/puck/[...all]/route.ts中的示例 Google 语境替换为关于你的产品、受众与内容规则的清晰描述并利用designMode.instructions约束 AI 生成组件的风格。使用真正的数据库。在lib/get-page.ts与app/api/pages/route.ts中替换database.json。本地文件在多个服务器实例或 serverless 部署环境下并不可靠无共享磁盘、实例可能随时回收。选择合适的渲染策略。app/[...puckPath]/page.tsx使用了force-static。如果某个页面需要请求期数据如 headers、cookies 或用户会话请移除该声明改回动态渲染。小结recipes/next-ai展示了一条完整的AI 驱动的可视化建站流水线proxy.ts用/edit约定把任意路径映射到 Puck 编辑器createAiPlugin与withDynamicConfig让 AI 既能拼装既有组件、也能动态生成新组件puckHandler在服务端桥接 Puck 云并注入业务上下文/api/pages负责持久化并通过revalidatePath让静态页面即时更新。理解这条链路后你可以在此基础上替换数据层、接入鉴权、扩充组件库把它演变为真正属于自己业务的可视化页面平台。【免费下载链接】puckThe visual editor for React.项目地址: https://gitcode.com/GitHub_Trending/puc/puck创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表