Yao SUI 前端 API 完整指南:组件查询、后端调用、渲染与 CUI 集成实战
发布时间:2026/9/28 3:40:47来源:尧图网络
Agent 框架后端低代码RAG【免费下载链接】yao✨ All your agents and workspaces in one place, on every device you own. Track tasks on a board, accessible from desktop, mobile, browser, or API. Self-hosted.项目地址https://gitcode.com/gh_mirrors/ya/yao点击查看免费下载本文以 sui/docs/frontend-api.md 为主体骨架结合 Yao 仓库中 sui/libsui 的前端运行时源码与 sui/api 的后端网关实现系统讲解 SUISingle User Interface前端 API 的完整能力组件实例查询、后端脚本调用、区域渲染Render、HTTP 客户端Yao SDK 与 OpenAPI Client、文件上传下载以及 SUI 页面嵌入 CUI 宿主/web/路由时的跨窗口通信机制。读完本文你将能够独立编写一个具备数据加载、状态响应、文件处理与宿主联动能力的完整 SUI 组件脚本。SUI 是 Yao 的前端渲染引擎页面由 HTML 模板、TypeScript 组件脚本与后端脚本共同构成。前端 API 是连接这三者的纽带通过$$()获取组件实例、通过$Backend().Call()与__sui_backend_call()调用后端Api前缀方法、通过self.render()局部刷新页面区域、通过OpenAPI/FileAPI完成标准 HTTP 交互。下面按文档脉络逐层展开并对照源码揭示每个 API 的底层行为。一、组件查询从选择器到组件实例1.1$$()函数$$()是 SUI 组件查询的核心入口支持两种入参CSS 选择器字符串与HTMLElement实例。返回值为封装后的组件包装对象__sui_component而非裸 DOM 元素// 按 ID 查询 const card $$(#my-card); // 按元素查询 const element document.querySelector(.card); const card $$(element); // 访问组件方法 card.toggle(); card.state.Set(expanded, true);对照 sui/libsui/index.ts 的$$实现可以看到其判定逻辑若入参是字符串则先执行document.querySelector(selector)随后读取元素上的s:cn属性组件名称若s:cn非空且window[cn]存在同名构造函数则new windowcn实例化组件并包装为__sui_component返回。若元素没有s:cn或组件构造器未注册$$()返回null——因此在使用前建议做空值保护。__sui_component包装对象暴露了四个核心成员index.tsroot组件根元素即带有s:cn属性的 DOM 节点store数据存储对象读写data:/json:属性props属性读取对象读写prop:属性state组件状态对象绑定watch监听器。1.2 查询方法组件包装对象还提供 DOM 查询与子组件查找能力const component $$(#my-component); // 查找子组件返回 __Query 包装 const button component.find(button); // 查询单个元素 const title component.query(.title); // 返回 Element // 查询所有元素 const items component.queryAll(.item); // 返回 NodeList其中find()返回的是__Query包装对象见 sui/libsui/utils.ts它比原生 DOM API 多出以下链式能力方法作用find(selector)/findAll(selector)在包装元素内继续查找返回__Queryclosest(selector)向上查找祖先返回__Queryon(event, cb)绑定事件监听返回this可链式调用$$()从当前元素向上定位最近的s:cn组件并返回$$()实例store()获取当前元素的数据存储data(key)/json(key)/prop(key)读取data:/json:/prop:属性addClass()/removeClass()/toggleClass()/hasClass()类名操作支持传入空格分隔的多个类名html(html?)读取或设置innerHTML值得留意的是__Query.$$()它通过this.element.closest([s\\:cn])向上找到组件根元素后再交给$$()非常适合在事件回调中从任意子元素反查所属组件。二、后端调用两种方式打通前后端SUI 页面中的前端脚本需要调用后端业务逻辑。后端以 TypeScript 脚本形式存在于页面源码旁方法名统一带Api前缀例如ApiGetUsers。前端提供两种调用通道。2.1 通过$Backend()调用import { $Backend } from yao/sui; // 后端函数为 ApiGetUsers、ApiGetUser、ApiCreateUser调用时无需前缀 const users await $Backend().Call(GetUsers); const user await $Backend().Call(GetUser, 123); const result await $Backend().Call(CreateUser, John, johnexample.com);$Backend()的实现在 sui/libsui/utils.ts它以当前页面路径为默认路由若body上声明了s:public则以其为公共根拼接构造__Backend对象Call(method, ...args)最终委托给全局函数__sui_backend_call。2.2 直接调用__sui_backend_call// __sui_backend_call(route, headers, method, ...args) // 注意方法名同样会被自动加上 Api 前缀 const result await __sui_backend_call( /users/list, // 页面路由 { X-Custom-Header: value }, // 自定义请求头 GetUsers, // 方法名后端实际为 ApiGetUsers { page: 1, limit: 10 } // 参数 );__sui_backend_call的实现位于 sui/libsui/index.ts它的行为对理解整个调用链至关重要请求地址固定为POST /v1/__yao/sui/v1/run{route}其中{route}即传入的页面路由请求头自动注入Content-Type: application/json、Referer当前页面 URL与Cookie再合并用户自定义 headers请求体{ method, args }args为展开的剩余参数数组错误处理HTTP 状态码 ≥ 400 时返回Promise.reject({ message, code })message优先取响应体中的message字段。后端侧该路由由 sui/api/api.go 中声明的/run/*route路径映射到sui.Run处理器实际逻辑见 sui/api/run.go。处理流程为解析路由对应页面的.cfg配置 → 执行 API Guard见下文→ 加载页面后端脚本 → 校验global.Has(prefix method)存在性 → 最终scriptCtx.Call(prefixmethod, args...)执行 V8 上下文中的方法。默认前缀是Api但若页面配置api.prefix非空则使用自定义前缀run.go。2.3 调用权限API Guard$Backend().Call()并非默认放行。页面.cfg配置中的api.defaultGuard与api.guards控制每个后端方法的访问权限详见 sui/docs/page-config.md{ guard: oauth, api: { defaultGuard: oauth, guards: { PublicSearch: - } } }上例中页面渲染与所有 API 方法默认要求 OAuth 认证而ApiPublicSearch方法例外放行-表示无需认证。内置 Guard 包括oauth、bearer-jwt、cookie-jwt、query-jwt、cookie-trace与-。Guard 的判定逻辑在 sui/api/run.go优先取api.guards[method]方法级配置其次api.defaultGuardoauthGuard 在认证通过后还会执行 ACL 权限校验enforceACL。对于-或空值直接放行其他自定义 Guard 名称则作为进程Process调用。三、Render API局部区域渲染3.1 渲染目标Render Target在 HTML 模板中通过s:render属性声明渲染目标div s:renderuserList classuser-list !-- 此处内容将被替换 -- /divself.render(userList, data)调用后前端将数据提交到服务端服务端渲染出新的 HTML 片段并回填到该区域。这是一个服务端模板渲染模型而不是纯前端 DOM 拼接。3.2 渲染方法import { $Backend, Component } from yao/sui; const self this as Component; self.RefreshUsers async () { const users await $Backend().Call(GetUsers); // 携带数据渲染 await self.render(userList, { users }); };3.3 渲染选项Render Optionsawait self.render(targetName, data, { replace: true, // 替换内容默认 true showLoader: true, // 显示加载指示器 withPageData: true, // 渲染上下文中包含页面数据 route: /custom/route, // 使用自定义路由渲染 });对照 sui/libsui/index.ts 的__sui_render实现可以确认以下行为默认值replace默认trueshowLoader默认falsewithPageData默认false对应RenderOption类型定义见 index.tsLoader 形态showLoader为true时默认使用span classsui-render-loadingLoading.../span也支持传入字符串或HTMLElement自定义数据合并渲染数据以comp.store.GetData()即json:__component_data为基底withPageData为真时再并入页面级__sui_data最后合并本次调用传入的data路由解析优先取根元素上s:route属性结合body[s:public]前缀否则取option.route兜底为当前window.location.pathname请求地址POST /v1/__yao/sui/v1/render{route}请求体为{ name, data, option }替换与初始化replace: true时将返回的 HTML 写入目标元素innerHTML随后自动初始化渲染片段内的子组件读取s:cn与s:ready执行构造器并调用__sui_event_init完成事件绑定渲染失败时写入span classsui-render-errorFailed to render/span。后端对应处理器为 sui/api/render.go 中的Render它先取得页面缓存Cache执行页面 Guard然后定位[s:rendername]元素用core.NewTemplateParser结合data渲染该选区最后返回替换后的 HTML 片段。这一设计让局部刷新同样可以复用页面级的主题、语言与权限上下文。四、HTTP 客户端Yao SDK 与 OpenAPI Client除 SUI 专属调用外前端还提供两个通用 HTTP 客户端。文档明确指出OpenAPIClient 为推荐方案YaoSDK 属于遗留Legacy实现。4.1 Yao SDK遗留方案Yao类提供轻量 HTTP 客户端能力实现在 sui/libsui/yao.tsconst yao new Yao(); // GET 请求 const data await yao.Get(/api/users, { page: 1 }); // POST 请求 const result await yao.Post(/api/users, { name: John }); // 下载文件 await yao.Download(/api/export, { format: csv }, export.csv); // Token 管理 const token yao.Token(); yao.SetCookie(key, value, 30); // 30 天过期 yao.DeleteCookie(key);其实现要点构造函数默认以window.location.protocol // window.location.host拼接/api作为 baseURLFetch会自动从Token()优先sessionStorage.token其次__tkCookie取出 token 并写入authorization: Bearer token请求头Download通过创建a元素并触发click()完成浏览器下载SetCookie默认 30 天有效期DeleteCookie通过把过期时间设为 1970 年来清除。4.2 OpenAPI Client推荐OpenAPI是现代化的 HTTP 客户端提供类型安全与统一错误处理实现在 sui/libsui/openapi.ts。初始化const api new OpenAPI({ baseURL: /api });HTTP 方法// GET const response await api.GetUser[](/users); // POST const response await api.PostUser(/users, { name: John, email: johnexample.com, }); // PUT const response await api.PutUser(/users/123, { name: John Updated, }); // DELETE const response await api.Deletevoid(/users/123);所有请求均携带credentials: include同源与跨域场景下都提交 CookieGET 通过URLSearchParams生成查询串DELETE 也支持可选的请求体openapi.ts。错误处理const response await api.GetUser[](/users); if (api.IsError(response)) { console.error(Error: ${response.error.error_description}); return; } const users response.data;IsError是类型守卫type guard通过response.error ! undefined判定openapi.ts配合GetData()可以安全地提取response.data。handleResponse内部会按Content-Type区分 JSON 与非 JSON 响应并生成parse_error/http_error等标准化错误对象openapi.ts。响应类型interface APIResponseT { data: T; } interface APIError { error: { error: string; error_description: string; }; }CSRF 与跨域OpenAPI会在每次请求前通过addCSRFToken注入X-CSRF-Token请求头token 来源优先级为安全 Cookie__Host-csrf_token/__Secure-csrf_token/__Host-xsrf_token/__Secure-xsrf_token→localStoragecsrf_token/xsrf_token→ 页面meta namecsrf-token/meta namexsrf-tokenopenapi.ts。IsCrossOrigin()通过比较baseURL解析出的 origin 与window.location.origin判断是否为跨域 APIopenapi.ts。五、File API文件上传、下载与管理FileAPI封装在OpenAPI之上默认使用__yao.attachment上传器uploader提供上传、下载、查询与工具方法sui/libsui/openapi.ts。5.1 初始化与上传const api new OpenAPI({ baseURL: /api }); const fileApi new FileAPI(api);const fileInput document.querySelectorHTMLInputElement(#file); const file fileInput.files[0]; // 带进度回调上传 const response await fileApi.Upload( file, { path: documents, groups: [team-a], compressImage: true, }, (progress) { console.log(${progress.percentage}%); } );FileUploadOptions支持的字段openapi.ts包括uploaderID、originalFilename、groups组权限逗号分隔写入表单、gzip压缩、compressImage/compressSize图片压缩、path存储路径、chunked/chunkSize分片上传、public、shareprivate | team。多文件上传const responses await fileApi.UploadMultiple( Array.from(fileInput.files), { path: uploads }, (fileIndex, progress) { console.log(File ${fileIndex}: ${progress.percentage}%); } );UploadMultiple内部为每个文件并行发起Upload并保留各自的进度回调openapi.ts。分片上传细节当options.chunked为真或文件超过默认 2MB 分片阈值时Upload自动切换为分片模式openapi.ts。分片逻辑以Content-Range: bytes start-end/total、Content-Sync: true、Content-Uid三个请求头标记分片序号与文件标识逐片通过 XHR 上传并汇报进度openapi.ts。5.2 文件操作// 列出文件 const files await fileApi.List({ page: 1, pageSize: 20, contentType: image/*, orderBy: created_at desc, }); // 获取文件信息 const info await fileApi.Retrieve(file-id); // 下载返回 Blob const blob await fileApi.Download(file-id); if (!api.IsError(blob)) { const url URL.createObjectURL(blob.data); window.open(url); } // 删除 await fileApi.Delete(file-id); // 检查是否存在 const exists await fileApi.Exists(file-id);各方法对应的 HTTP 端点如下openapi.ts方法端点ListGET /file/{uploaderID}参数映射为page、page_size、status、content_type、name、order_by、selectRetrieveGET /file/{uploaderID}/{fileID}DownloadGET /file/{uploaderID}/{fileID}/contentDeleteDELETE /file/{uploaderID}/{fileID}ExistsGET /file/{uploaderID}/{fileID}/exists5.3 工具方法// 格式化文件大小 FileAPI.FormatSize(1024); // 1 KB FileAPI.FormatSize(1048576); // 1 MB // 获取扩展名 FileAPI.GetExtension(doc.pdf); // pdf // 类型判断 FileAPI.IsImage(image/png); // true FileAPI.IsDocument(application/pdf); // trueFormatSize按 1024 进制依次输出 Bytes/KB/MB/GB/TB 并保留两位小数IsDocument内置了 PDF、Word、Excel、PowerPoint、纯文本、CSV 等常见文档类型清单openapi.ts。六、跨域支持与令牌管理const api new OpenAPI({ baseURL: https://api.example.com }); if (api.IsCrossOrigin()) { console.log(Cross-origin API); } // 登录成功后设置 CSRF token const loginResponse await api.Post(/auth/login, credentials); if (!api.IsError(loginResponse) loginResponse.data.csrf_token) { api.SetCSRFToken(loginResponse.data.csrf_token); } // 退出时清除令牌 api.ClearTokens();SetCSRFToken将 token 写入localStorage.csrf_tokenClearTokens同时清除csrf_token与xsrf_tokenopenapi.ts。这一组合适用于前后端跨域部署如前端静态托管、后端独立域的场景登录接口返回 CSRF token 后由前端显式保存后续所有请求自动携带X-CSRF-Token。七、自定义事件与状态变化事件7.1 发送与监听事件import { Component } from yao/sui; const self this as Component; // 发送事件 self.Select () { self.emit(card:selected, { id: self.store.Get(id) }); };import { Component } from yao/sui; const self this as Component; // 监听事件 self.root.addEventListener(card:selected, (e: CustomEvent) { console.log(Selected:, e.detail.id); });emit的实现即new CustomEvent(name, { detail: data })并在组件根元素上dispatchEventindex.ts因此监听端使用标准addEventListener(card:selected, ...)读取e.detail即可。7.2 状态变化事件// 监听子组件状态变化 self.root.addEventListener(state:change, (e: CustomEvent) { const { key, value, target } e.detail; console.log(${key} ${value}); });state:change由__sui_state.Set在状态写入后向上传播产生index.ts当watch中注册了对应 key 的处理器时先执行处理器若处理器的stateObj.stopPropagation()未被调用则沿组件树向上找到最近的父组件closest([s\\:cn])并派发state:change事件detail携带{ key, value, target }。这是实现父子组件数据联动子改状态、父组件响应的关键机制。八、完整示例一个可运行的数据 CRUD 组件文档给出的完整示例串起了上述全部 APIimport { $Backend, Component, EventData } from yao/sui; const self this as Component; // 初始化 API const api new OpenAPI({ baseURL: /api }); const fileApi new FileAPI(api); // 状态监听器 self.watch { users: (users: any[]) self.render(userList, { users }), loading: (loading: boolean) { self.root.classList.toggle(loading, loading); }, }; // 加载用户 async function loadUsers() { self.state.Set(loading, true); const response await api.GetUser[](/users); if (!api.IsError(response)) { self.state.Set(users, response.data); } self.state.Set(loading, false); } // 创建用户 self.CreateUser async (event: Event, data: EventData) { const response await $Backend().Call(CreateUser, data.name, data.email); const users self.state.Get(users); self.state.Set(users, [...users, response]); }; // 上传头像 self.UploadAvatar async (event: Event) { const input event.target as HTMLInputElement; const file input.files![0]; const response await fileApi.Upload(file, { path: avatars }); if (!api.IsError(response)) { self.emit(avatar:uploaded, { url: response.data.url }); } }; // 初始化 loadUsers();组件脚本的运行前提是页面 HTML 中存在s:on-xxx事件绑定如s:on-clickCreateUser、s:on-changeUploadAvatar。事件绑定的初始化由__sui_event_init完成index.ts它扫描带有s:event/s:event-jit属性的元素收集s:on-*属性确定事件名与处理函数收集data:*/json:*属性组装事件数据然后按最近的s:cn组件实例化并绑定监听器。若s:event-cn为__page则直接在window上查找处理函数并以document.body为根。九、CUI 集成/web/路由下的宿主通信当 SUI 页面通过/web/路由嵌入 CUIYao 的 Web 客户端宿主时页面可以读取宿主注入的参数并与之双向通信。9.1 URL 参数替换CUI 会自动替换以下特殊参数值值替换为__theme当前主题light/dark__locale当前语言环境如en-us注意认证使用安全的 HTTP-only Cookie无需传递 token 参数。9.2 接收来自 CUI 的消息window.addEventListener(message, (e) { // 仅接受同源消息 if (e.origin ! window.location.origin) return; const { type, message } e.data; switch (type) { case setup: // CUI 提供的初始上下文 document.documentElement.setAttribute(data-theme, message.theme); console.log(Locale:, message.locale); break; case update: // CUI 推送的数据更新 handleUpdate(message); break; } });setup与update是 CUI 宿主向 SUI 页面推送的两类消息前者在页面加载时携带主题与语言等初始上下文后者用于数据变更通知。9.3 向 CUI 发送动作Actions使用统一的 Action 系统触发宿主操作// 辅助函数 const sendAction (name: string, payload?: any) { window.parent.postMessage( { type: action, message: { name, payload } }, window.location.origin ); }; // 通知 sendAction(notify.success, { message: Operation completed! }); sendAction(notify.error, { message: Something went wrong }); // 页面导航 sendAction(navigate, { route: /agents/my-app/detail, title: Details, query: { id: 123 }, }); // 新标签页打开 sendAction(navigate, { route: /agents/my-app/report, target: _blank, }); // 刷新菜单 sendAction(app.menu.reload); // 关闭侧边栏 sendAction(event.emit, { key: app/closeSidebar, value: {} });9.4 可用 Actions 一览类别Action说明Payload导航navigate在侧边栏/标签页打开页面{ route, title?, icon?, query?, target? }navigate.back返回历史-通知notify.success成功通知{ message, duration?, closable? }notify.error错误通知{ message, duration?, closable? }notify.warning警告通知{ message, duration?, closable? }notify.info信息通知{ message, duration?, closable? }应用app.menu.reload刷新应用菜单-模态框modal.open打开模态对话框{ ... }modal.close关闭模态框-表格table.search触发表格搜索{ keywords }table.refresh刷新表格数据-表单form.submit提交表单-form.reset重置表单-事件event.emit发送自定义事件{ key, value }确认confirm显示确认对话框{ title, content }9.5 CUI 集成完整示例import { $Backend, Component, EventData } from yao/sui; const self this as Component; // 辅助函数向 CUI 发送动作 const sendAction (name: string, payload?: any) { window.parent.postMessage( { type: action, message: { name, payload } }, window.location.origin ); }; // 初始化 CUI 通信 function init() { window.addEventListener(message, (e) { if (e.origin ! window.location.origin) return; if (e.data.type setup) { const { theme, locale } e.data.message; document.documentElement.setAttribute(data-theme, theme); } }); (window as any).sendAction sendAction; } init(); // 事件处理 self.HandleSave async (event: Event, data: EventData) { try { await $Backend().Call(Save, data); sendAction(notify.success, { message: Saved successfully! }); } catch (error: any) { sendAction(notify.error, { message: error.message }); } }; self.HandleViewDetail (event: Event, data: EventData) { sendAction(navigate, { route: /agents/my-app/detail, title: Details, query: { id: data.id }, }); }; self.HandleClose () { sendAction(event.emit, { key: app/closeSidebar, value: {} }); };十、前端 API 的底层调用链速览综合文档与源码SUI 前端 API 的后端支撑可归纳为以下两条核心链路路由声明见 sui/api/api.go均由POST触发后端调用链$Backend().Call()/__sui_backend_call()→POST /v1/__yao/sui/v1/run/{route}→sui.Runsui/api/run.go→ 加载页面.cfg→ API Guard 校验 → 加载后端脚本 → V8 上下文执行Api{Method}渲染调用链self.render(name, data, option)→POST /v1/__yao/sui/v1/render/{route}→sui.Rendersui/api/render.go→ 页面缓存与 Guard →core.NewTemplateParser渲染[s:render]选区 → 返回 HTML 片段 → 前端替换innerHTML并初始化子组件与事件。页面.cfg配置文件*.cfg是上述两条链路的枢纽它决定了页面的 Guard、缓存策略cache/dataCache、SEO 信息与 API 前缀配置细节可继续阅读 sui/docs/page-config.md。前端运行时源码集中在 sui/libsui/index.ts组件查询、状态、渲染、后端调用、sui/libsui/utils.ts__Query、$Backend、$Store与 sui/libsui/openapi.tsOpenAPI、FileAPI组件脚本的结构约定可参考 sui/docs/components.md。掌握以上 API 后你便可以在 Yao SUI 中编写出具备完整交互能力的页面组件用$$()定位组件、用state/store管理数据、用$Backend()与OpenAPI读写后端、用render()局部刷新、用FileAPI处理附件并在嵌入 CUI 时通过 Action 系统与宿主协同工作。赞分享Agent 框架后端低代码RAG【免费下载链接】yao✨ All your agents and workspaces in one place, on every device you own. Track tasks on a board, accessible from desktop, mobile, browser, or API. Self-hosted.项目地址https://gitcode.com/gh_mirrors/ya/yao点击查看免费下载相关推荐Yao SUI 后端脚本Backend Scripts完全指南服务端逻辑、数据绑定与 API 端点开发实战Yao SUI 后端脚本Backend Scripts完全指南服务端逻辑、数据绑定与 API 端点开发实战 SUISimple User InterfaAgent 框架后端低代码RAGDear ImGui渲染后端集成实战Dear ImGui渲染后端集成实战 本文深入解析了Dear ImGui的渲染后端架构设计与实现原理详细介绍了OpenGL和Vulkan后端的集成配置方法并UI组件前端桌面应用图形学CopilotKit 工具渲染实战用 useRenderTool 将后端 Agent 工具调用渲染为 React 组件CopilotKit 工具渲染实战用 useRenderTool 将后端 Agent 工具调用渲染为 React 组件 在后端 Agent 工具调用的全流程中人工智能AI AgentAgent 框架前端后端上一篇华硕笔记本终极控制方案5步掌握GHelper轻量级控制工具下一篇智能游戏存档管理解决方案Ludusavi实现跨平台进度保护自动化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网