Skip to content

AI自定义组件

AI自定义组件是一种不存储业务值、专门负责表单展示与交互的字段类型。你可以用自然语言让 AI 生成界面,也可以继续修改 Vue、React 或 uni-app 代码;组件运行时会自动获得当前记录和字段上下文,不需要自己配置网站地址或 IFC。


一、先用一句话理解它

先看一个直观的例子

假设我们有一张“新闻”表,里面保存了标题、概要、发布日期、创建人等字段。使用普通表单时,打开一条新闻记录后,这些字段通常会按照输入框、日期等标准控件逐项排列。数据虽然完整,但整体看起来更像一张“录入表单”,不适合作为面向阅读者的内容展示。

添加前:新闻内容由普通字段逐项展示。

添加AI自定义组件字段前的新闻表单

如果希望同一条新闻以卡片形式展示——标题突出显示、概要限制行数,底部整齐排列创建人和发布时间,并且自动适应表单宽度——就需要把多个字段重新组合成一块自定义 UI。此时可以在新闻表中新建一个 AI自定义组件 字段,用自然语言描述想要的卡片效果,让 AI 生成界面。系统会自动把当前新闻记录交给组件渲染,原有字段仍然负责保存数据。

添加后:AI自定义组件把多个字段重新组织成一张新闻卡片。

添加AI自定义组件字段后的新闻卡片

第四节会带你完整实现这个新闻摘要卡片。

普通字段只能按照系统预设的输入框、日期、附件、关联记录等形式展示数据;AI自定义组件则允许你把当前这一条记录重新组织成任意界面,例如:

  • 把学生姓名、学号、班级、照片组合成一张学生信息卡;
  • 把新闻标题、摘要、发布时间和作者展示成门户风格的新闻卡片;
  • 把设备状态、负责人、故障次数做成带颜色和图标的状态面板;
  • 根据表单权限显示“通过”“驳回”“标记已处理”等操作;
  • 在保存表单前,对组件内的业务规则进行补充校验。

它不是把一个普通网站生硬地塞进字段,而是让 AI 网站进入“AI自定义组件模式”:系统会把关联数据表、可用字段、当前记录、当前用户、主题和运行时权限作为固定背景提供给 AI 和组件代码。

它最适合解决什么问题

当你遇到下面任意一种情况时,可以考虑使用 AI自定义组件:

  1. 普通字段能存数据,但展示方式达不到业务要求;
  2. 一个区域需要同时展示当前记录的多个字段;
  3. 需要在表单记录页面显示更复杂的卡片、图表、进度、状态或响应式布局;

它不适合什么场景

  • 只需要输入一个普通文本、日期、数字时,直接使用系统字段更简单;
  • 需要展示整张表的多条记录时,优先考虑表格、看板、仪表盘或独立 AI 网站;
  • 需要一个有导航、登录页和多个路由的完整应用时,应直接使用 AI 网站
  • 需要在表格列表中作为普通列展示时,不要使用本字段。AI自定义组件只在记录表单中显示,不在表格列表中显示。

二、它与“自定义组件”“AI 网站”有什么区别

对比项AI自定义组件自定义组件普通 AI 网站
主要用途重做一条记录在表单中的展示与交互通过网站页面、IFC 或组件定义自定义字段构建独立页面、看板或应用
创建方式AI 生成,也可手工改代码需要理解原有组件配置方式AI 生成,也可手工改代码
数据上下文自动注入当前记录、字段、用户、主题和权限取决于原配置与接入方式默认是独立网站上下文
关联范围一个组件固定关联一张数据表按原自定义组件配置通常不固定到某张表
显示位置记录表单记录表单独立模块页面或访问地址
列表显示不显示取决于原字段实现不适用
访问权限固定为“应用访问成员”按原模块配置可选应用成员、登录用户或公开访问
后续维护继续用 AI、代码、ZIP 或 Git使用原组件/网站开发方式继续用 AI、代码、ZIP 或 Git

推荐原则

如果目标是“美化或增强当前记录表单里的一个区域”,优先使用 AI自定义组件;如果目标是“做一个可以独立打开的业务页面”,使用普通 AI 网站。


三、开始前需要知道的 6 条规则

3.1 一个 AI 网站组件只能关联一张表

例如“学生信息卡”绑定了“学生表”,它就只能被“学生表”里的 AI自定义组件字段选择,不能同时给“教师表”使用。

同一个组件可以被同一张表中的多个 AI自定义组件字段绑定。

3.2 被字段使用后,组件与数据表会锁定

当至少一个 AI自定义组件字段引用该 AI 网站模块后:

  • 不能关闭“允许作为AI自定义组件”;
  • 不能更换关联数据表;
  • 不能删除该 AI 网站模块。

如需执行上述操作,应先把所有引用字段更换为其它组件并保存,或者删除不再需要的 AI自定义组件字段。字段设置中的“解除绑定”只会临时清空当前选择;AI自定义组件字段必须绑定组件才能保存,因此它不能单独作为最终的解引用方式。

3.3 复制出的模块可以重新选表

复制 AI 网站模块后,副本拥有新的模块 ID,也没有字段引用。因此可以先修改副本关联的数据表和字段范围,再把它绑定到新表中的字段。

3.4 已有记录通常会提供记录 ID

打开已经保存的记录时,无论选择“全部字段”还是“仅指定字段”,record.id 都会保留。新建记录在首次保存前可能还没有 ID,因此不要把“是否存在 record.id”作为页面能否使用的判断条件。其它业务字段严格以组件的“数据范围”为准。

3.5 调试数据不是业务数据

AI 网站设计器里的调试数据只是一个 JSON 快照,用于让预览有真实感。组件放进表单后,系统会用当前记录的真实上下文覆盖调试数据。

3.6 设计态只读不等于运行时一定不可编辑

字段最终是否可见、是否可编辑,可能受到表单模式、字段权限、当前用户、当前记录、流程节点和表达式影响。组件代码应在运行时调用 isFieldVisibleisFieldEditable 判断,不能只看字段设计中的静态只读属性。


四、快速入门:做一张新闻摘要卡片

下面用一张“新闻”表为例,把标题、概要、创建人和创建时间组合成一张新闻摘要卡片。

4.1 新建字段

  1. 进入应用设计器,打开目标数据表;
  2. 进入“表单字段”;
  3. 在“控件类”中选择“AI自定义组件”;
  4. 填写字段名称,例如“新闻摘要卡片”;
  5. 根据表单布局设置显示宽度。

字段本身不存储数据库值,它只是当前记录界面的一个展示区域。

4.2 选择“用AI创建”

在“绑定的UI组件”区域点击“用AI创建”。如果已经有同表组件,也可以选择“选择已有组件”,详见 第五节

4.3 设置技术栈

系统会弹出“创建AI自定义组件”窗口:

创建AI自定义组件项目配置

设置选项作用
目标终端PC端 / 移动端决定 AI 默认采用的布局密度和交互方式
框架Vue 3 / React / uni-app决定项目脚手架和 UI 组件库
语言JavaScript / TypeScript决定源码语言
主题色颜色作为按钮、强调状态等默认颜色
国际化开启 / 关闭开启后 AI 优先生成可扩展的多语言文案结构
可用 npm 依赖只读列表当前编译环境允许安装和使用的依赖

不确定如何选择时,直接使用默认配置:PC端 + Vue 3 + JavaScript + Element Plus

技术栈会固化

组件创建后,框架等技术偏好会作为项目基础固定下来。需要更换框架时,建议新建组件,不要在原项目中混用两套脚手架。

AI自定义组件只能在应用表单中运行,因此访问权限固定为“应用访问成员”。

点击“保存并创建”后,系统会自动完成以下工作:

  1. 创建一个 AI 网站模块;
  2. 开启该模块的 AI自定义组件模式;
  3. 自动关联当前数据表;
  4. 把当前字段、表结构、容器设置写入生成背景;
  5. 自动把新模块绑定到当前字段;
  6. 立即打开 AI 网站设计器。

模块默认名称格式为:

text
<表名>-<字段名>的自定义组件-<4位随机数字>

例如:班·墨要闻-新闻摘要卡片的自定义组件-4831

4.4 写清楚第一轮需求

进入 AI 网站设计器后,不需要再解释“这是哪个应用、哪张表、有哪些字段”。系统已经把这些内容作为固定提示背景提供给 AI。

可以直接输入:

请为当前记录做一张简洁的新闻摘要卡片。标题最多显示两行,概要最多显示四行;底部显示创建人和创建时间。没有概要时显示“暂无概要”。使用当前应用主题色作为强调色,宽度跟随容器,不要创建导航、登录页、路由、后端脚本或 API。

一个好需求通常包含 5 类信息:

  1. 展示什么:明确字段和信息层级;
  2. 长什么样:卡片、图表、时间线、状态面板等;
  3. 如何适配:PC、移动端、窄容器、长文本;
  4. 如何交互:只读展示还是允许修改字段;
  5. 空值与异常:字段为空、权限不足、加载失败时怎么显示。

4.5 预览并继续修改

AI 会读取当前组件模式、数据表和字段范围,生成前端代码。完成后在右侧预览:

AI自定义组件模式与新闻卡片预览

顶部“AI自定义组件模式”栏会显示:

  • 当前关联的数据表;
  • 当前传入“全部字段”还是“指定 N 个字段”;
  • 当前被多少个 AI自定义组件字段引用;
  • “数据范围”和“调试数据”入口。

如果第一版不满意,可以继续说:

标题字号再小一点;概要超过四行时省略;创建时间使用当前语言环境格式;在宽度小于 480px 时把底部信息改成两行。

4.6 保存字段并发布应用

回到字段设置,确认组件与高度模式后保存字段:

已绑定组件的字段设置

在 AI 网站设计器中先确认编译成功,再回到应用设计器发布应用。


五、选择已有组件

点击“选择已有组件”或已绑定卡片上的“更换组件”,会打开组件库:

选择已有AI自定义组件

组件库只显示同时满足以下条件的 AI 网站模块:

  1. 已开启“允许作为AI自定义组件”;
  2. 已关联当前字段所在的数据表;

绑定其它数据表的组件不会显示在这里。列表卡片展示组件名称、版本号和绑定字段数;单击后点“使用此组件”,也可以直接双击选择。

为什么不显示所有 AI 网站

AI自定义组件的上下文是按数据表生成并校验的。限制为同表组件可以避免把“学生表组件”错误绑定到“教师表字段”,也能让提示词、调试数据和运行时字段范围保持一致。


六、字段设置详解

6.1 绑定的UI组件

这是必选项。未绑定组件时字段不能保存。

绑定后卡片会显示:

  • AI 网站模块名称;
  • 当前草稿版本;
  • 最近更新时间(按浏览器本地时区和格式显示);
  • 被多少个 AI自定义组件字段绑定。

可执行的操作:

操作作用注意事项
AI编辑打开当前 AI 网站设计器新建字段应先保存,再从字段卡片进入编辑
更换组件从同表组件库中重新选择只改变当前字段绑定,不会删除原模块
解除绑定清空当前字段的模块绑定解除后必须重新选择组件才能保存字段

6.2 高度模式

AI自定义组件在表单中通过隔离的页面容器运行。高度模式决定容器如何占用表单空间。

固定高度

  • 由“固定高度”设置容器高度;
  • 可设置范围为 120~1200 px;
  • 适合图表、地图、滚动列表或希望布局稳定的组件;
  • 内容超过容器时,应在组件内部设计滚动区域。

跟随内容自适应

  • 平台自动检测页面真实内容高度并调整字段容器;
  • 高度仍限制在 120~1200 px;
  • 适合信息卡、审批摘要、动态表单块;
  • 组件代码通常不需要调用 reportHeight

自适应高度不是“无限高度”

如果内容非常长,最终仍会受最大高度保护。长列表应使用分页、折叠或内部滚动,不要一次渲染几百行。

6.3 通用字段设置

AI自定义组件仍然拥有普通字段的通用设置,例如:

  • 字段名称和字段标识符;
  • 在表单中的显示宽度;
  • 名称表达式;
  • 字段描述和描述展示位置;
  • 表单权限、可见性和只读规则。

这些设置控制字段在表单外层的行为;组件内部仍应根据运行时上下文决定具体界面。


七、AI 网站中的组件模式

7.1 从字段“用AI创建”进入

这是推荐方式。系统会自动创建模块、关联表、固定访问权限并打开设计器,最不容易配错。

7.2 把已有 AI 网站改成组件

如果已经有一个 AI 网站项目,也可以在 AI 网站设计器的“设置 → 网站智能体”中开启“允许作为AI自定义组件”。首次开启时必须选择一张关联数据表和字段范围。

开启后,系统会重新编译项目,并在设计器顶部显示组件模式栏。

改造已有网站前先检查布局

普通 AI 网站可能包含全屏高度、导航、路由或登录页。组件运行在字段容器中,开启模式后应让 AI 删除独立网站式外壳,改为适应窄容器的单页组件布局。

7.3 组件模式对 AI 做了什么

每一轮对话都会自动附带运行协议,明确告诉 AI:

  • 当前页面运行在表单字段中,不是独立网站;
  • 当前关联表、字段元数据和有效数据范围;
  • window.__informat__.customUI 读取上下文;
  • 当前记录直接来自 context.record,不要重复查询;
  • 修改字段前必须检查运行时权限;
  • 监听记录和字段变化,不能只读取第一次数据;
  • 不要主动创建导航、登录页、全屏外壳、后端脚本或 API;
  • 设计器调试数据不能写死到业务代码中。

因此,用户通常只需要描述业务目标和界面效果,不必每次重复技术契约。


八、组件数据范围:决定页面真正能拿到什么

在 AI 网站设计器顶部点击“数据范围”:

组件数据范围

数据范围同时作用于 3 个地方:

  1. AI 生成时看到的表字段背景;
  2. 设计器“调试数据”可编辑的字段;
  3. 发布态表单注入给组件的真实字段和字段变化事件。

8.1 全部字段

  • 当前表的全部可用业务字段都会进入组件上下文;
  • 以后新增字段也会自动进入;
  • 适合组件需要广泛展示当前记录,且开发者会持续维护代码的场景。

8.2 仅指定字段

  • 只传入明确勾选的字段;
  • 新增字段不会自动进入;
  • 未勾选字段不会出现在 table.fieldsrecord 和字段变化事件中;
  • 适合安全边界明确、组件用途稳定的场景。

例如新闻卡片只需要“标题、概要、创建人、创建时间”,就只勾选这 4 个字段。

8.3 记录 ID 与系统字段

打开已有记录时,record.id 用于标识当前记录;新建记录在首次保存前可能没有 ID。系统内部的 IDSEQ 不会作为普通调试字段展示;不要依赖内部序号完成业务逻辑。

8.4 数据范围如何保护发布态

发布态加载组件前,后端会再次校验:

  • 当前字段是不是 AI自定义组件;
  • 字段绑定的模块 ID 是否一致;
  • 模块是否处于组件模式;
  • 模块关联的数据表是否一致;
  • 当前发布版本允许传入哪些字段。

只有校验通过才会加载组件。真实记录会在父表单侧按字段范围裁剪后再注入;校验不一致时系统不会回退为“全部字段”。

数据范围不等于额外 API 权限

数据范围严格限制系统自动注入的当前记录上下文。若你另外编写脚本或 API 查询其它数据,仍需遵守应用的数据权限和接口权限。组件代码也不应通过额外接口绕过当前字段范围去查询被排除的字段。


九、调试数据:让预览不再全是空值

在 AI 网站设计器顶部点击“调试数据”:

AI自定义组件调试数据

调试数据有 3 种准备方式:

9.1 手动填写

系统会根据字段类型生成输入控件:文本、数字、开关、日期、时间、颜色等可以直接编辑;附件、人员、部门、关联记录等复杂字段使用 JSON 输入。

9.2 选择现有数据

点击“选择现有数据”,从关联表中选择一条真实记录。系统会读取当前数据范围内的字段,并转换成可继续编辑的 JSON 快照。

这通常是最快的调试方法,因为可以立即覆盖真实业务中的长标题、空字段、附件、人员等情况。

9.3 填入示例值

点击“恢复示例”可以根据字段类型重新生成一组示例数据,适合还没有业务记录时快速检查布局。

9.4 快照的保存规则

  • 保存后立即同步到当前 AI 网站预览;
  • 快照保存在当前 AI 网站模块中;
  • 来源记录之后发生变化,不会自动同步;
  • 可以点击“重新读取原记录”主动更新;
  • 发布态表单会用当前真实记录覆盖快照。

建议准备 3 组调试场景

至少测试“正常数据”“大量长文本”“大量空值”三种情况。如果组件允许编辑,再增加“无编辑权限”场景进行真实表单验证。


十、可直接复用的提示词模板

只描述需求,不需要描述技术实现

使用下面的模板时,只需要把【】中的内容替换成自己的业务信息。你不需要告诉智能体应该调用什么函数、监听什么事件,也不需要说明组件如何与表单通信;这些运行规则会由 AI自定义组件模式自动提供给智能体。

只读信息卡

请把当前这条【业务对象】记录展示成一张清晰美观的信息卡。展示【字段列表】,其中【主字段】重点突出,【次要字段】适当弱化。字段为空时显示【空值文案】,长文本最多显示【N】行。整体采用【简洁 / 正式 / 活泼】风格,使用应用主题色,并兼顾电脑和手机上的显示效果。

可编辑状态卡

请为当前【业务对象】生成一张【业务状态】卡片,展示【字段列表】。根据【状态字段】的不同值使用不同颜色显示状态,并提供【按钮列表】。用户点击【按钮名称】时,把【状态字段】改为【目标值】;如果当前用户不能修改这个字段,就只展示状态,不显示相关按钮。操作成功时提示【成功文案】,失败时给出容易理解的说明。界面需要兼顾电脑和手机。

带保存校验的交互组件

请为当前【业务对象】制作一个【交互界面】。用户需要完成【选择 / 填写动作】,并把结果保存到【字段名称】。如果用户尚未完成这一步,保存记录时应阻止保存,并提示【错误文案】。如果当前用户不能编辑相关字段,就只展示已有结果,不显示输入和提交操作。


推荐先让 AI 完成开发

对于信息卡片、状态面板、数据摘要、简单操作按钮等常见需求,推荐的最佳实践是直接在 AI 网站设计器中描述业务目标,让 AI 编写代码、设计页面并根据反馈继续修改。你只需要说明希望展示哪些内容、采用什么布局、不同状态如何呈现、用户可以执行哪些操作,以及空值和移动端应如何处理。AI自定义组件模式会自动向 AI 提供当前数据表、可用字段、记录上下文和必要的运行规则,因此无须先学习组件与表单之间的技术细节,也无须在提示词中指定具体的函数、事件或实现方式。通常只需通过几轮自然语言沟通,就可以完成组件的生成、预览和调整。

如果需求包含更复杂的业务逻辑、精细的数据联动或特殊的交互规则,或者你希望由开发人员直接阅读、修改并长期维护源码,可以继续阅读下面的高阶内容。后续章节将介绍组件运行时能够获取哪些数据、如何响应记录和字段的变化、如何与父表单交互并修改字段,以及如何实现保存前校验等开发能力。这些内容主要面向需要深度定制或自行开发的用户,并不是使用 AI自定义组件的前置要求;不编写代码的用户可以跳过这些章节,继续通过 AI 完成页面迭代。

十一、组件运行时可以获得哪些内容

组件通过以下对象访问平台上下文:

javascript
const customUI = window.__informat__ && window.__informat__.customUI
if (!customUI) {
  throw new Error('当前页面没有可用的 AI自定义组件上下文')
}

const context = await customUI.ready()
if (!context || context.available === false) {
  // 显示降级界面,并继续监听后续 context-change;连接恢复后仍可重新渲染
  console.warn('组件上下文暂时不可用')
}

context 使用版本化契约,当前为 contractVersion: 1。主要内容如下:

属性主要内容常见用途
available当前上下文是否可用初始化失败或组件独立打开时做降级处理
record父表单当前内存中的记录和数据范围内的字段值渲染卡片、状态、图表;新建记录可能暂时没有 ID
field当前 AI自定义组件字段定义和设置获取字段名、容器高度模式等;设计器中的模拟定义可能更精简
table表 ID、标识符、名称,以及数据范围内的字段元数据动态渲染字段、识别类型;运行时 table.fields 只保证 idkeynametype 等基础信息
app应用 ID、名称、主题色统一品牌与强调色
user当前用户 ID、名称和 logo个性化展示
theme明暗模式、主色适配明暗主题
permissions当前上下文的整体编辑与交互摘要editable: true 只表示数据范围内至少有一个字段可编辑;不能代替字段级权限检查
scene上下文所在环境,例如 designerformwebsite区分设计器、真实表单和独立打开等载体
mode当前页面模式,例如 previewcreateeditviewstandalone区分预览和表单工作模式;不能据此断定某个字段可编辑
locale当前语言环境格式化日期、数字和文案

一个简化的上下文示例:

json
{
  "contractVersion": 1,
  "available": true,
  "scene": "form",
  "mode": "edit",
  "locale": "zh_CN",
  "app": {
    "id": "app-id",
    "name": "校园信息平台",
    "color": "rgb(46, 102, 83)",
    "colorKey": "C1"
  },
  "table": {
    "id": "student-table",
    "key": "student",
    "name": "学生",
    "fields": [
      { "id": "field-name", "key": "name", "name": "姓名", "type": "SingleText" },
      { "id": "field-class", "key": "className", "name": "班级", "type": "SingleText" }
    ]
  },
  "field": {
    "id": "custom-ui-field",
    "key": "studentCard",
    "name": "学生信息卡",
    "type": "CustomUI"
  },
  "record": {
    "id": "record-id",
    "name": "张同学",
    "className": "软件技术 1 班"
  },
  "user": {
    "id": "user-id",
    "name": "李老师",
    "logo": "/path/to/avatar.png"
  },
  "theme": {
    "mode": "light",
    "primaryColor": "#2e6653"
  },
  "permissions": {
    "editable": true,
    "interactive": true
  }
}

不要写死调试上下文

不要把调试窗口中的学生姓名、记录 ID、主题色或用户信息复制到代码常量中。初始化时调用一次 ready() 获取上下文;后续直接使用 context-change 的事件载荷。只有确有一次性主动刷新需求时才调用 getContext()

数据范围与运行时权限是两件事

table.fieldsrecord 表示组件最多能获得哪些字段,不代表这些字段在当前表单里一定可见或可编辑。显示某个字段前调用 isFieldVisible(fieldKey);提供写入操作前调用 isFieldEditable(fieldKey)。生成提示中可能包含字段设计态的静态 readonly,但真实运行时 context.table.fields 不保证携带该属性,也不能用它代替动态权限检查。


十二、常用 API 与事件

12.1 API 一览

javascript
const customUI = window.__informat__ && window.__informat__.customUI
API作用返回值/说明
ready()等待首个组件上下文仅用于初始化;返回 Promise<Context>,连接不可用时可能得到 available: false
getContext()主动请求当前完整上下文返回 Promise<Context>;成功后会静默刷新本地缓存,不会自行触发 context-change。初始化后通常应直接使用事件载荷
getRecord()读取父表单当前内存记录只含允许字段,以及已有记录可能具有的 ID;值可能尚未保存到数据库
getField()读取当前组件字段定义返回字段对象
getTableInfo()读取当前表和有效字段RPC 成功时返回 { appId, originalAppId, table, tableFieldList };降级结果可能只有缓存表信息,使用前应检查结构
isFieldVisible(fieldKey)判断字段在当前真实表单是否可见fieldKey 必须是字段标识符;范围外、不存在、连接失败或超时都返回 false
isFieldEditable(fieldKey)判断字段在当前真实表单是否可编辑fieldKey 必须是字段标识符;范围外、不存在、动态只读、连接失败或超时都返回 false
setFieldValue(fieldKey, value)修改父表单当前内存值true 只表示修改请求被接受,不代表记录已保存;无效、范围外、不可编辑、连接失败或超时返回 false
showLoading()让父表单显示组件加载状态返回 Promise<boolean>
hideLoading()关闭组件加载状态返回 Promise<boolean>
registerValidator(fn)注册或替换一个父表单保存前校验函数同步方法、无返回值;函数可同步或异步返回布尔值或 { valid, message? },推荐使用对象形式提供提示文案;传入 null 可清除校验函数
on(type, handler)监听事件返回取消监听函数
off(type, handler)取消事件监听不传 handler 时清除该类监听
reportHeight(height)兼容旧组件手动上报内容高度返回 Promise<boolean>;仅内容自适应模式有效,父表单会把高度限制在 120~1200 px

onoffregisterValidator 外,上表方法都是异步方法,请使用 await。调用父表单的 RPC 方法具有 5 秒保护:发生异常或超时时,读取方法会返回缓存值或不可用上下文,布尔操作会返回 false,而不是抛出异常。因此,不能只用 try...catch 判断调用是否成功,false 也可能表示组件连接异常或超时。

reportHeight() 是底层兼容接口。当前平台会自动检测内容高度,业务代码通常不应主动调用。

12.2 监听上下文变化

同一个组件页面可能随着用户切换记录、编辑字段或表单重载而继续复用。ready() 只负责第一次初始化,后续变化应通过事件更新。

javascript
const customUI = window.__informat__ && window.__informat__.customUI

function render(context) {
  if (!context || context.available === false) {
    // 显示“组件上下文暂时不可用”等降级界面
    return
  }
  // 使用 context.record 更新页面
}

const initialContext = await customUI.ready()
render(initialContext)

const stopContext = customUI.on('context-change', context => {
  render(context)
})

const stopField = customUI.on('field-change', payload => {
  // 可选:处理与单个字段变化有关的轻量副作用。
  // 完整页面会由随后到达的 context-change 刷新。
})

// 组件卸载时调用
// stopContext()
// stopField()

常用事件及其载荷:

事件载荷使用建议
context-change完整 context作为刷新整个组件界面的主要事件
field-change字段变化信息;真实表单和设计器模拟载荷略有差异把它当作“某个字段已变化”的信号,不要依赖固定的 field 数据类型
record-reload{ type, field, record }处理父表单重新加载记录等场景

事件处理函数应保持幂等,因为初始化、连接恢复或同一轮上下文合并可能提供内容相同的通知。context-change 的载荷已经是最新完整上下文,应直接使用。当前运行时的 getContext() 不会再次派发事件,但在每次事件中重复调用仍会制造不必要的 RPC、重叠的异步读取和额外渲染;旧的发布版本还可能形成事件反馈。因此,不要在 context-changefield-change 回调里无条件调用 ready()getContext()getRecord()

12.3 修改父表单字段

下面示例把“处理状态”改为“已处理”:

javascript
const fieldKey = 'status'
const customUI = window.__informat__.customUI

const editable = await customUI.isFieldEditable(fieldKey)
if (!editable) {
  console.warn('当前用户或当前表单场景不可编辑该字段')
  return
}

const success = await customUI.setFieldValue(fieldKey, 'done')
if (!success) {
  console.warn('字段修改未被接受,请检查字段、权限、数据范围和组件连接状态')
}

需要注意:

  • fieldKey 使用字段标识符,不是字段名称;
  • 字段必须在组件数据范围内;
  • 字段在当前表单中必须可编辑;
  • true 只表示父表单接受了本次内存修改请求,记录仍需按照当前表单的手动保存或自动保存机制写入数据库;
  • 关联记录、查找列表、子对象等复杂字段在返回 true 后仍可能继续异步转换,应再检查父表单中的实际结果;
  • 要确认数据已经持久化,应保存父表单,并关闭后重新打开同一条记录验证;
  • 不要直接访问 window.parent 或修改父页面 DOM。

12.4 注册表单校验

如果组件允许用户选择一个结果,并要求选择后才能保存表单:

javascript
const customUI = window.__informat__.customUI

customUI.registerValidator(async () => {
  const record = await customUI.getRecord()
  if (!record || !record.reviewResult) {
    return {
      valid: false,
      message: '请先选择审核结果'
    }
  }
  return { valid: true }
})

同一页面同一时间只保留一个校验函数,后注册的函数会替换前一个。校验函数应尽快返回,不要执行长时间网络请求:异常或 5 秒超时会按校验失败处理,父表单最多等待 6 秒。没有注册校验器时默认通过。校验依赖的字段也必须在组件数据范围内。

12.5 组件生命周期与资源释放

表单切换记录、重新加载或反复打开抽屉时,组件可能多次初始化和卸载。所有“注册”操作都应有对应的“释放”操作,否则监听、定时器和第三方组件实例会逐渐累积,最终表现为重复渲染、数据跳回旧记录、页面卡顿或内存持续增长。

请遵守以下规则:

  1. ready() 只在组件初始化时调用一次;不要在渲染函数、监听回调、watcheffect 中重复调用;
  2. on() 只注册一次,并保存它返回的取消监听函数;
  3. 优先调用自己保存的取消函数,不要随意使用 off('context-change') 清除该类型的全部监听,以免误删项目中其它代码注册的处理器;
  4. 组件卸载时,清理 setInterval、递归 setTimeoutrequestAnimationFramewindow.addEventListenerResizeObserverMutationObserver 等资源;
  5. ECharts、地图、富文本编辑器等第三方实例应调用自身的 dispose()destroy() 等销毁方法;
  6. 如果注册过保存校验器,在功能关闭或组件卸载时调用 registerValidator(null) 清除它;
  7. 如果事件回调中包含异步处理,应避免旧记录的迟到结果覆盖新记录。可以记录一次更新序号,只接受最后一次处理结果。

下面是一个与框架无关的生命周期示例:

javascript
const customUI = window.__informat__?.customUI
const disposers = []
let active = true

function applyContext(context) {
  if (!active || !context || context.available === false) return
  // 使用 context.record 更新响应式状态
}

async function initCustomUI() {
  if (!customUI) return

  // 先注册恢复事件,再等待第一次上下文。
  disposers.push(customUI.on('context-change', applyContext))

  const initialContext = await customUI.ready()
  applyContext(initialContext)
}

function disposeCustomUI() {
  active = false
  disposers.splice(0).forEach(dispose => dispose())
  customUI?.registerValidator(null)
}

在 Vue 3 中,分别从 onMountedonBeforeUnmount 调用 initCustomUI()disposeCustomUI();Vue 2 可使用 mountedbeforeDestroy;React 应在 useEffect 的清理函数中释放资源。applyContext() 应保持幂等,即使收到内容相同的上下文也不会重复创建实例或叠加副作用。

12.6 禁止使用的高频写法

AI自定义组件是事件驱动的页面。父表单只在记录、字段或权限真正发生变化时推送上下文,不需要组件反复询问父表单。下面这些写法会造成大量无效 RPC、重复渲染和资源泄漏,应当避免。

不要轮询上下文、记录、权限或高度

javascript
// 错误:即使页面没有任何变化,也会一直跨 iframe 调用父表单。
setInterval(async () => {
  const context = await customUI.getContext()
  const editable = await customUI.isFieldEditable('status')
  render(context, editable)
}, 600)

应在初始化时读取一次上下文,后续使用 context-change 的载荷;字段真正写入前,再调用一次 isFieldEditable() 复核权限。不要使用 setInterval、递归 setTimeoutrequestAnimationFrame 轮询 ready()getContext()getRecord()isFieldVisible()isFieldEditable()setFieldValue()reportHeight()

不要在上下文事件里再次主动读取上下文

javascript
// 错误:事件载荷已经是最新完整上下文,这次请求完全多余。
customUI.on('context-change', async () => {
  render(await customUI.getContext())
})

// 正确:直接使用事件载荷。
customUI.on('context-change', context => {
  render(context)
})

field-change 只适合处理确有必要的单字段轻量副作用。不要同时在 field-changecontext-change 中执行整页渲染,否则一次字段修改可能触发两次昂贵更新。

不要在初始化、渲染或事件回调中自动写回字段

setFieldValue() 只能由用户点击按钮、提交输入等明确的交互操作触发。不要在 ready() 完成后、页面渲染时、context-changefield-changewatcheffect、定时器或自动重试中调用它。父表单接受字段修改后会产生新的字段和上下文变化,如果事件处理器再次自动写回,就可能形成“写入 → 事件 → 再写入”的反馈循环。

javascript
// 错误:每次收到上下文都再次写回,可能形成反馈循环。
customUI.on('context-change', async context => {
  const nextStatus = normalizeStatus(context.record.status)
  await customUI.setFieldValue('status', nextStatus)
})

正确做法是在用户操作发生时检查一次权限,并为一次操作只发送一次修改请求:

javascript
let submitting = false

async function handleSetDoneClick() {
  if (submitting) return
  submitting = true

  try {
    const editable = await customUI.isFieldEditable('status')
    if (!editable) {
      console.warn('当前用户不能修改状态')
      return
    }

    const accepted = await customUI.setFieldValue('status', 'done')
    if (!accepted) {
      console.warn('修改未被接受,请检查连接、字段范围和权限')
    }
  } finally {
    submitting = false
  }
}

false 可能表示字段不可编辑、连接异常或调用超时,不能用无限自动重试来解决;应保留当前界面,让用户在连接恢复或问题修正后重新操作。true 也只表示父表单接受了本次内存修改请求,不代表记录已经持久化。按钮应在请求进行时禁用或防抖,避免一次点击产生多次请求。

不要在渲染或更新过程中重复注册监听

javascript
// 错误:每次 render 都会增加一个新的匿名监听函数。
function render() {
  customUI.on('context-change', context => updatePage(context))
}

监听应在组件初始化阶段注册一次,并在组件卸载时取消。Vue 的 watchwatchEffect、React 的重复 effect 中也不要无清理地注册监听、定时器或全局事件。

不要自行循环测量并上报高度

javascript
// 错误:平台已经管理内容高度,这会产生重复观察和高度反馈循环。
const observer = new ResizeObserver(() => {
  customUI.reportHeight(document.body.scrollHeight)
})
observer.observe(document.body)

当前平台会自动观察 #app 的真实内容高度。业务代码通常不应调用 reportHeight(),也不应为高度另建轮询或观察器。需要改善高度时,应优先删除业务根节点上的 100vh、固定大高度、不必要的 min-height、外边距和内边距。

此外,不要在高频事件中打印或 JSON.stringify 整份上下文,不要反复创建图表、编辑器、地图实例,也不要为当前记录重复发起脚本或 API 请求。必须处理大量数据时,应使用分页、折叠、虚拟列表或合并更新。

设计器预览不能替代真实表单测试

没有真实父表单 RPC 时,设计器中的 setFieldValue 只修改当前预览内存里的模拟记录;isFieldVisibleisFieldEditable 也只是基于字段范围和整体模拟权限的近似结果。registerValidator 不会经历真实的表单保存流程,内容高度也不是最终表单容器高度。写回、动态权限、保存校验和高度必须在发布后的真实表单中复测。


十三、示例二:真实完成一张可交互的学生状态卡

本例要把姓名、学号、班级、照片和学籍状态组合成一张学生状态卡。除了展示信息,卡片还会在 status 运行时可编辑时显示三个按钮,让用户直接把学籍状态设为“在读”“休学”或“毕业”。

13.1 创建学生表和字段

本例实际创建的数据表名为“学生状态卡实测”,包含下面 6 个业务字段:

字段标识符类型说明
姓名name单行文本卡片主标题
学号studentNo单行文本学生编号
班级className单行文本用于验证普通文本和长文本布局
学籍状态status列表选择在读、休学、毕业
照片photo附件有照片时显示真实附件,没有时显示姓名首字
学生状态卡studentStatusCardAI自定义组件负责组合展示和状态交互,不存储业务值

学生状态卡实测表的完整字段

“学籍状态”不是直接保存中文,而是保存稳定的选项标识符。本例使用:

显示名称选项标识符
在读enrolled
休学suspended
毕业graduated

这一步很重要。AI自定义组件收到的是字段实际值,按钮写回时也必须传实际值,因此提示词里要把标识符与中文名称的映射写清楚。

13.2 准备三条真实测试记录

为了覆盖正常数据、空附件和长文本,本例实际插入了三条记录:

姓名学号班级照片最终状态主要验证目标
张晓然20260001软件技术 1 班休学真实照片、字段同步、状态写回和持久化
李沐20260002数字媒体技术 2 班休学无照片时的姓名首字占位
欧阳晨20260003人工智能技术应用(校企联合培养实验班)毕业长姓名、长班级和毕业状态写回

第一条记录最初的状态是“在读”。后续在真实表单中点击卡片的“设为休学”后,父表单和卡片一起变成“休学”,关闭并重新打开记录后仍然保持“休学”。第三条记录也通过卡片按钮从空状态改成了“毕业”。

从学生表中选择真实测试记录

13.3 从字段中创建 AI自定义组件

在“学生状态卡”字段的“绑定的UI组件”区域点击“用AI创建”,本例选择:

  • 目标终端:PC端;
  • 框架:Vue 3;
  • 语言:JavaScript;
  • 主题色:#409EFF
  • 国际化:关闭。

学生状态卡的创建配置

保存后,系统实际创建的 AI 网站模块名称是:

text
学生状态卡实测-学生状态卡的自定义组件-8773

模块名称末尾的随机数字可以降低重复创建时重名的概率。系统同时把模块关联到“学生状态卡实测”表、绑定到当前字段,并立即打开 AI 网站设计器。通用项目配置的作用见 4.3 设置技术栈

字段高度模式选择“跟随内容自适应”,让表单中的页面容器跟随卡片真实内容高度变化。

13.4 只传入组件真正需要的字段

在 AI 网站设计器顶部打开“数据范围”,选择“仅指定字段”,只勾选:

  • name
  • studentNo
  • className
  • status
  • photo

学生状态卡只传入五个指定字段

保存后,设计器提示“待传入 5 个字段和记录 ID”。本次还直接检查了设计器上下文:record 中包含记录 ID 和上述 5 个字段,没有创建时间、创建人等未勾选字段。数据范围的一般规则见 第八节

13.5 选择一条现有记录作为调试数据

打开“调试数据”,点击“选择现有数据”,选择有照片的“张晓然”。系统把当前数据范围内的真实字段转换成可编辑快照:

从真实学生记录生成调试数据快照

从截图可以看到:

  • 姓名、学号和班级已自动填入;
  • status 的值是 "enrolled",不是中文“在读”;
  • photo 是附件对象数组,不是一个普通图片网址;
  • 快照来源记录显示为“张晓然”。

保存快照后,设计器预览有了与真实记录一致的数据。以后修改这份快照只会影响设计器预览;发布态仍由当前表单记录覆盖。详细规则见 第九节

13.6 本例实际发送给 AI 的需求

第一轮实际需求同时写清楚了字段、状态映射、空值处理、响应式布局和交互规则。下面的内容可以直接作为类似需求的起点;组件与表单通信的技术规则会由系统自动提供给 AI,无须在需求中重复函数和事件名称:

text
请为当前学生记录生成一张紧凑、精致的个人状态卡,直接修改当前项目并完成编译。

布局要求:左侧显示圆形照片,右侧显示姓名、学号、班级和有颜色的学籍状态标签;没有照片时用姓名首字作为头像占位。附件 photo 是对象数组,优先读取第一个附件中的可用缩略图或路径;如果无法组成可访问地址,就稳定显示姓名首字,不要显示破图。长姓名和长班级必须自动换行,窄容器时改成上下布局;不要使用 100vh,页面高度随内容自适应。

交互要求:只有当前用户在当前表单中可以修改“学籍状态”时,才显示“设为在读”“设为休学”“设为毕业”三个按钮;只读时只展示状态。三个按钮对应写入的实际值分别是 enrolled、suspended、graduated。操作被表单接受后立即刷新卡片并提示成功;操作未被接受时给出容易理解的说明。父表单记录或权限发生变化后,卡片也要自动更新。

13.7 预览

最终预览中,调试快照里的真实照片、姓名、学号、班级和“在读”状态均正常显示:

AI网站设计器中的学生状态卡最终预览

13.8 发布后在真实表单中检查

发布应用后,打开“张晓然”的真实记录。普通字段仍然负责显示和保存原始数据,AI自定义组件则在“学生状态卡”字段中重新组织同一组数据:

发布态真实表单中的学生状态卡

截图中可以同时看到:

  • 父表单的姓名、学号、班级、学籍状态和照片;
  • 卡片中的同一组字段;
  • 卡片使用的是真实附件缩略图;
  • 卡片状态与父表单状态一致;
  • 三个状态按钮只在 status 运行时可编辑时显示。

本例使用“跟随内容自适应”。实际检查时,组件 iframe 和外层字段容器会收缩到约 178 px,而不是继续占用默认的 320 px 固定高度。

13.9 验证父表单变化会同步到卡片

在不修改组件代码的情况下,把父表单“班级”临时改成“软件技术 1 班(国际合作方向)”,卡片立即显示同样的内容:

父表单班级变化后卡片立即同步

验证完成后,本例又把班级恢复成“软件技术 1 班”。这一步证明组件监听了父表单字段变化,显示的是当前表单内存中的最新值,而不是设计器保存的调试快照。

13.10 验证状态按钮写回和持久化

第一条记录最初是“在读”。在真实表单中点击“设为休学”后:

  1. 组件发出的状态修改请求被父表单接受;
  2. 父表单“学籍状态”立即变成“休学”;
  3. 卡片状态标签同时变成“休学”;
  4. 页面显示“已设为「休学」”的成功提示。

点击卡片按钮后父表单和卡片同时变成休学

当前测试应用的记录抽屉采用字段变化自动保存,因此修改时间随之更新。关闭并重新打开同一条记录后,父表单和卡片仍然都是“休学”,说明本次修改已经持久化:

重新打开记录后休学状态仍然保留


十四、版本、编辑与发布

AI自定义组件复用 AI 网站的工程能力,因此同样支持:

  • 对话继续修改;
  • 代码工作区手工编辑;
  • 编译与实时预览;
  • 版本快照和回滚;
  • 下载、导入项目源码;
  • Git 同步。

详细用法可继续阅读 AI 网站(AI Code Studio)

版本与回滚的边界

  • 可以随时手动创建检查点,保存当前草稿源码和项目配置;
  • 应用发布时,只有组件项目成功构建,才会自动形成对应的发布快照;
  • 回滚版本只会把目标版本恢复到设计器草稿,并重新编译预览,不会直接改变已经上线的版本;
  • 回滚后如果要让真实表单使用该版本,必须重新发布应用。

AI自定义组件模式下,AI 网站的访问范围固定为“应用访问成员”,不能改成公开访问或任意登录用户。这个访问范围只决定谁能加载组件;组件内某个字段能否编辑,仍由真实表单的动态权限决定。

推荐发布流程

  1. 在 AI 网站设计器中准备调试数据;
  2. 完成第一版组件并确认编译成功;
  3. 测试 PC、平板、手机视口;
  4. 为稳定版本创建检查点;
  5. 回到应用设计器发布应用;
  6. 在真实表单中测试创建、编辑、查看、动态权限、内容高度和字段写回;
  7. 如需修正,重新编译、创建新检查点并再次发布;
  8. 连续切换至少 3 条记录、重复打开和关闭记录 10 次,并让页面空闲 30~60 秒;确认数据不会串到旧记录、组件高度稳定、空闲时没有持续 RPC 或事件、浏览器内存不会持续增长;
  9. 用具有应用访问权限的普通业务用户账号做最终验收。

预览成功和应用发布完成都不能替代发布态验收

预览使用的是可编辑调试快照;发布态使用已发布的字段绑定、组件设置、数据范围、当前记录和动态表单权限。应用发布流程整体完成,也不必然表示每个 AI 网站构建产物都成功生成。发布后必须打开一条真实记录确认组件能够加载;如加载失败,先查看 AI 网站的编译与发布日志,修复后重新编译并发布。


十五、常见问题与排查

15.1 “选择已有组件”里找不到某个 AI 网站

依次检查:

  1. AI 网站的项目配置是否已经保存;
  2. 模块是否确实为 AI 网站、没有被删除;
  3. 是否开启“AI自定义组件模式”;
  4. 关联数据表是否与当前字段所在表完全一致;
  5. 关闭并重新打开组件库;仍未出现时再刷新设计器页面。

组件库读取的是当前设计草稿,不要求先发布应用。绑定其它数据表的 AI 网站不会显示。

15.2 预览里所有内容都是空的

先按下面顺序检查:

  1. 打开“调试数据”,选择一条现有记录或手动填写数据并保存;
  2. 确认当前代码已经编译成功,并查看编译日志是否有错误;
  3. 确认组件入口已经挂载到 #app,并且初始化时等待了组件上下文;
  4. 确认“数据范围”包含页面实际读取的字段;
  5. 检查代码是否在上下文不可用时错误地继续读取空对象。

不要为了让预览有内容而把示例值写死进代码。调试快照不会进入发布态。

15.3 设计器正常,真实表单显示不同

这是最常见的差异。检查:

  • 是否已经发布应用;
  • 已发布的字段绑定、组件模式、关联表和数据范围是否保持一致;
  • 发布态数据范围是否包含所需字段,当前记录是否确实有值;
  • 当前用户是否具有应用访问权限;
  • 页面初始化获得的上下文是否 available: true
  • 组件是否监听 context-change
  • 是否错误地依赖了调试数据;
  • 展示和操作是否分别依据 isFieldVisibleisFieldEditable 处理动态表单权限。

数据范围决定哪些字段会被注入,动态可见性和可编辑性不会再从 record 中删除字段;组件代码需要自行尊重运行时权限。

15.4 点击按钮不能修改父表单

检查:

  1. 字段是否在数据范围内;
  2. 是否使用字段标识符;
  3. await isFieldEditable(key) 是否返回 true
  4. 当前表单是不是只读/查看模式;
  5. setFieldValue 返回值是否为 true;如果为 false,还要排查组件连接或 5 秒超时;
  6. 是否尝试直接操作父页面而没有使用桥接 API。

true 只表示父表单接受了本次内存修改请求,不代表已经写入数据库。返回 true 后还应检查父表单值是否变化,并按照当前表单的保存机制保存;最后关闭并重新打开记录,确认修改已经持久化。列表选择等字段要写入实际选项值,附件、关联记录、子对象等复杂字段还要使用平台要求的数据结构。

15.5 自适应高度仍然不理想

  • 确认字段选择“跟随内容自适应”;
  • 删除组件根节点不必要的固定高度或 min-height: 100vh
  • 检查组件自己的 paddingmargin
  • 避免把弹窗、下拉菜单永久放在文档流中;
  • 长列表使用折叠、分页或内部滚动。

平台会把组件页面 htmlbodymarginpadding 都清零,但 #app 和业务子容器自己设置的 marginpadding、固定高度、min-height 仍会计入内容高度。父表单最终会把自适应高度限制在 120~1200 px:内容少于 120 px 时仍会保留最小高度,超过 1200 px 时应使用折叠、分页或内部滚动。首次高度上报前可能短暂使用字段原有的容器高度。

15.6 为什么表格列表里看不到这个字段

这是预期行为。AI自定义组件用于记录表单,不作为表格列表列渲染。如果需要列表卡片或整页多记录展示,请使用表格/卡片视图、仪表盘或普通 AI 网站。

15.7 为什么不能关闭组件模式、改表或删除模块

该模块仍被一个或多个 AI自定义组件字段引用。应逐个打开引用它的字段,选择“更换组件”并保存;如果字段本身已经不需要,也可以删除该 AI自定义组件字段。字段设置中的“解除绑定”只会临时清空当前选择,而未绑定组件的 AI自定义组件字段无法保存,因此单独点击它不能完成最终解引用。确认页面显示“被 0 个AI自定义组件字段绑定”后,再关闭组件模式、改表或删除模块。

15.8 代码改了但预览没有变化

先区分三种情况:

  1. 编译失败:先查看并修复编译日志;清缓存不能修复代码错误;
  2. 编译成功但设计器预览仍旧:保存文件后点击“编译并刷新”,必要时使用“清除缓存”重新加载预览 iframe;
  3. 真实表单仍是旧版本:设计器的“清除缓存”不会更新发布态,应重新发布应用。确认已经重新发布仍未更新时,再硬刷新、清理站点缓存或使用无痕窗口验证。

15.9 页面卡顿、资源占用过高或组件偶发崩溃

先打开浏览器开发者工具查看控制台中的第一条异常。后续错误经常只是第一条异常造成的连锁反应,优先修复第一条错误比反复刷新页面更有效。

然后根据现象检查代码:

现象优先检查
页面空闲时 CPU 仍然很高搜索 setInterval、递归 setTimeoutrequestAnimationFrame,确认没有轮询桥接 API
RPC 或消息数量持续增长搜索 getContextgetRecordisFieldEditableisFieldVisiblereportHeight,检查它们是否位于定时器或事件回调中
字段值反复变化、消息成倍增长搜索 setFieldValue,确认它只由用户显式交互触发,没有放在 ready、上下文事件、watcheffect、定时器或自动重试中形成写回反馈循环
每打开一次记录就更卡搜索 customUI.onaddEventListenerResizeObserverMutationObserver,确认每次注册都有且只执行一次的清理逻辑
高度上下抖动或反复留白搜索 reportHeight、自建 ResizeObserver100vh、固定 heightmin-height,删除与平台自动高度重复的逻辑
数据偶尔为空检查是否把第一次 available: false 永久当成最终结果;降级显示后仍应等待后续 context-change 恢复真实数据
切换记录后又跳回旧数据检查事件回调中的异步请求,防止旧请求迟到后覆盖较新的上下文
内存持续上涨或 iframe 崩溃检查图表、地图、编辑器、大图片、定时器、监听和观察器是否在卸载时释放;不要在每次事件中重新创建重型实例

可以用下面的基线判断组件是否健康:

  • 父表单没有变化时,组件不应持续调用桥接 API,也不应不断收到 context-change
  • 内容高度不变时,组件不应反复测量或上报高度;
  • 切换记录后,页面只应显示最新记录,不应被较早的异步结果覆盖;
  • 连续打开、关闭记录后,iframe、全局监听和第三方实例数量不应累积;
  • 组件在设计器和真实表单中都应能从 available: false 的降级状态恢复,而不是用轮询等待;
  • 长时间空闲后,浏览器内存可以小幅波动,但不应随着时间或打开次数持续单向增长。

排查事件频率时,可以短时间使用 console.count('context-change') 等计数方式,并在定位完成后删除。不要长期在高频回调中打印整份 context,因为控制台保留大量对象本身也会增加内存和卡顿。postMessage RPC 不一定显示为普通网络请求,因此不能只看“网络”面板;还应结合浏览器的“性能”“内存”和“事件监听器”工具检查。

如果临时移除定时器、field-change 处理器或第三方图表后问题消失,再逐项恢复代码,可以快速确定是通信循环、重复监听还是重型渲染导致的问题。禁止通过增加更短的轮询间隔来“修复”数据为空,这通常只会放大性能和竞态问题。规范写法见 12.5 组件生命周期与资源释放12.6 禁止使用的高频写法


十六、设计与开发最佳实践

16.1 把组件当作“字段区域”,不要当作独立网站

  • 不做顶部导航、侧边栏和登录页;
  • 不使用 100vh 撑满浏览器;
  • 优先使用响应式网格、弹性布局和内容高度;
  • 控制信息密度,与表单其它字段保持协调。

16.2 当前记录优先使用上下文

当前记录已经由父表单提供,直接读取 context.record。除非需求明确涉及其它记录或聚合数据,否则不要为当前记录重复创建脚本/API。

16.3 所有写入都先检查权限

显示字段前调用 isFieldVisible,显示写入按钮前调用 isFieldEditablesetFieldValue 返回 true 只表示父表单接受了本次内存修改请求;是否持久化还取决于当前表单的保存机制。权限可能在用户切换、流程推进或记录变化后改变,因此需要监听上下文变化。

16.4 为范围外字段设计降级

不要假设字段永远存在。访问前先检查:

javascript
const hasSummary = context.table.fields.some(field => field.key === 'summary')
const summary = hasSummary ? context.record.summary : ''

16.5 正确处理复杂字段

人员、部门、附件、关联记录等通常是对象或数组。先在调试数据中选择一条真实记录,观察实际结构,再让 AI 或代码按结构渲染。

16.6 保留可回滚节点

在第一版可用、增加写入操作、重构布局、发布上线前分别创建版本检查点。复杂组件建议同时接入 Git。

16.7 使用事件更新,不使用轮询

初始化时调用一次 ready(),后续直接使用 context-change 的完整载荷。不要使用定时器、动画帧或观察器反复读取上下文、记录、权限或高度;真正执行字段写入前,再调用一次 isFieldEditable() 复核权限。详细的禁止写法见 12.6 禁止使用的高频写法

16.8 卸载时释放全部资源

监听、定时器、全局事件、观察器、校验器以及图表、地图、编辑器实例都必须在组件卸载时释放。注册和释放应成对出现,不能依赖刷新 iframe 被动清理。建议统一保存取消函数,并在一个卸载入口中集中执行,详见 12.5 组件生命周期与资源释放